@hyperfrontend/project-scope 0.2.1 → 0.2.3

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 (443) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/README.md +3 -4
  3. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.cjs.js +7 -0
  4. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.esm.js +5 -0
  5. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/console/index.cjs.js +13 -0
  6. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/console/index.esm.js +8 -0
  7. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/date/index.cjs.js +10 -0
  8. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/date/index.esm.js +8 -0
  9. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js +6 -0
  10. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.esm.js +5 -0
  11. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/json/index.cjs.js +7 -0
  12. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/json/index.esm.js +5 -0
  13. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/map/index.cjs.js +6 -0
  14. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/map/index.esm.js +5 -0
  15. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.cjs.js +9 -0
  16. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.esm.js +6 -0
  17. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/number/index.cjs.js +7 -0
  18. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/number/index.esm.js +7 -0
  19. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.cjs.js +15 -0
  20. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.esm.js +9 -0
  21. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/set/index.cjs.js +6 -0
  22. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/set/index.esm.js +5 -0
  23. package/_dependencies/@hyperfrontend/logging/index.cjs.js +191 -0
  24. package/_dependencies/@hyperfrontend/logging/index.d.ts +151 -0
  25. package/_dependencies/@hyperfrontend/logging/index.esm.js +186 -0
  26. package/_shared/core/cache/index.cjs.js +135 -0
  27. package/_shared/core/cache/index.esm.js +128 -0
  28. package/_shared/core/errors/structured-errors/index.cjs.js +28 -0
  29. package/_shared/core/errors/structured-errors/index.esm.js +23 -0
  30. package/_shared/core/fs/guard/index.cjs.js +7 -0
  31. package/_shared/core/fs/guard/index.esm.js +5 -0
  32. package/_shared/core/fs/stat/index.cjs.js +47 -0
  33. package/_shared/core/fs/stat/index.esm.js +41 -0
  34. package/_shared/core/path/confine/index.cjs.js +22 -0
  35. package/_shared/core/path/confine/index.esm.js +19 -0
  36. package/_shared/core/path/join/index.cjs.js +13 -0
  37. package/_shared/core/path/join/index.esm.js +10 -0
  38. package/_shared/core/path/normalize/index.cjs.js +37 -0
  39. package/_shared/core/path/normalize/index.esm.js +31 -0
  40. package/_shared/core/path/resolve/index.cjs.js +47 -0
  41. package/_shared/core/path/resolve/index.esm.js +41 -0
  42. package/_shared/core/path/segments/index.cjs.js +38 -0
  43. package/_shared/core/path/segments/index.esm.js +31 -0
  44. package/_shared/core/patterns/glob/index.cjs.js +145 -0
  45. package/_shared/core/patterns/glob/index.esm.js +136 -0
  46. package/_shared/core/platform/detect/index.cjs.js +103 -0
  47. package/_shared/core/platform/detect/index.esm.js +95 -0
  48. package/_shared/core/platform/line-endings/index.cjs.js +52 -0
  49. package/_shared/core/platform/line-endings/index.esm.js +44 -0
  50. package/_shared/project/config/patterns/index.cjs.js +172 -0
  51. package/_shared/project/config/patterns/index.esm.js +169 -0
  52. package/_shared/tech/monorepo/pnpm-workspaces/index.cjs.js +33 -0
  53. package/_shared/tech/monorepo/pnpm-workspaces/index.esm.js +31 -0
  54. package/_shared/tech/shared-utils/detector-helpers/index.cjs.js +48 -0
  55. package/_shared/tech/shared-utils/detector-helpers/index.esm.js +43 -0
  56. package/_shared/vfs/types/index.cjs.js +14 -0
  57. package/_shared/vfs/types/index.esm.js +12 -0
  58. package/cli/index.cjs.js +1706 -1910
  59. package/cli/index.d.ts +273 -7
  60. package/cli/index.d.ts.map +1 -1
  61. package/cli/index.esm.js +1593 -1797
  62. package/core/encoding/index.cjs.js +86 -401
  63. package/core/encoding/index.d.ts +186 -3
  64. package/core/encoding/index.d.ts.map +1 -1
  65. package/core/encoding/index.esm.js +78 -392
  66. package/core/fs/index.cjs.js +243 -583
  67. package/core/fs/index.d.ts +479 -6
  68. package/core/fs/index.d.ts.map +1 -1
  69. package/core/fs/index.esm.js +233 -573
  70. package/core/index.cjs.js +526 -1746
  71. package/core/index.d.ts +486 -9
  72. package/core/index.d.ts.map +1 -1
  73. package/core/index.esm.js +515 -1732
  74. package/core/path/index.cjs.js +8 -234
  75. package/core/path/index.d.ts +327 -5
  76. package/core/path/index.d.ts.map +1 -1
  77. package/core/path/index.esm.js +8 -235
  78. package/core/platform/index.cjs.js +5 -216
  79. package/core/platform/index.d.ts +185 -3
  80. package/core/platform/index.d.ts.map +1 -1
  81. package/core/platform/index.esm.js +3 -212
  82. package/heuristics/dependencies/index.cjs.js +105 -493
  83. package/heuristics/dependencies/index.d.ts +99 -2
  84. package/heuristics/dependencies/index.d.ts.map +1 -1
  85. package/heuristics/dependencies/index.esm.js +81 -469
  86. package/heuristics/entry-points/index.cjs.js +93 -796
  87. package/heuristics/entry-points/index.d.ts +123 -2
  88. package/heuristics/entry-points/index.d.ts.map +1 -1
  89. package/heuristics/entry-points/index.esm.js +74 -777
  90. package/heuristics/framework/index.cjs.js +1483 -1411
  91. package/heuristics/framework/index.d.ts +104 -2
  92. package/heuristics/framework/index.d.ts.map +1 -1
  93. package/heuristics/framework/index.esm.js +1419 -1347
  94. package/heuristics/index.cjs.js +3220 -3306
  95. package/heuristics/index.d.ts +4 -5
  96. package/heuristics/index.d.ts.map +1 -1
  97. package/heuristics/index.esm.js +3244 -3330
  98. package/heuristics/project-type/index.cjs.js +1489 -1438
  99. package/heuristics/project-type/index.d.ts +64 -2
  100. package/heuristics/project-type/index.d.ts.map +1 -1
  101. package/heuristics/project-type/index.esm.js +1419 -1368
  102. package/index.cjs.js +3058 -3532
  103. package/index.d.ts +44 -10
  104. package/index.d.ts.map +1 -1
  105. package/index.esm.js +2914 -3376
  106. package/models/index.cjs.js +0 -1
  107. package/models/index.d.ts +20 -14
  108. package/models/index.d.ts.map +1 -1
  109. package/models/index.esm.js +0 -1
  110. package/nx/index.cjs.js +166 -579
  111. package/nx/index.d.ts +279 -4
  112. package/nx/index.d.ts.map +1 -1
  113. package/nx/index.esm.js +148 -556
  114. package/package.json +13 -12
  115. package/project/config/index.cjs.js +127 -1063
  116. package/project/config/index.d.ts +202 -4
  117. package/project/config/index.d.ts.map +1 -1
  118. package/project/config/index.esm.js +110 -1044
  119. package/project/index.cjs.js +328 -1101
  120. package/project/index.d.ts +4 -5
  121. package/project/index.d.ts.map +1 -1
  122. package/project/index.esm.js +307 -1077
  123. package/project/package/index.cjs.js +196 -473
  124. package/project/package/index.d.ts +280 -3
  125. package/project/package/index.d.ts.map +1 -1
  126. package/project/package/index.esm.js +183 -459
  127. package/project/root/index.cjs.js +109 -417
  128. package/project/root/index.d.ts +83 -2
  129. package/project/root/index.d.ts.map +1 -1
  130. package/project/root/index.esm.js +98 -406
  131. package/project/traversal/index.cjs.js +96 -622
  132. package/project/traversal/index.d.ts +165 -3
  133. package/project/traversal/index.d.ts.map +1 -1
  134. package/project/traversal/index.esm.js +82 -608
  135. package/tech/backend/index.cjs.js +223 -508
  136. package/tech/backend/index.d.ts +205 -8
  137. package/tech/backend/index.d.ts.map +1 -1
  138. package/tech/backend/index.esm.js +202 -487
  139. package/tech/build/index.cjs.js +350 -636
  140. package/tech/build/index.d.ts +276 -10
  141. package/tech/build/index.d.ts.map +1 -1
  142. package/tech/build/index.esm.js +328 -614
  143. package/tech/frontend/index.cjs.js +507 -685
  144. package/tech/frontend/index.d.ts +379 -15
  145. package/tech/frontend/index.d.ts.map +1 -1
  146. package/tech/frontend/index.esm.js +483 -661
  147. package/tech/index.cjs.js +1581 -1420
  148. package/tech/index.d.ts +55 -32
  149. package/tech/index.d.ts.map +1 -1
  150. package/tech/index.esm.js +1515 -1354
  151. package/tech/legacy/index.cjs.js +99 -449
  152. package/tech/legacy/index.d.ts +125 -7
  153. package/tech/legacy/index.d.ts.map +1 -1
  154. package/tech/legacy/index.esm.js +81 -431
  155. package/tech/linting/index.cjs.js +138 -523
  156. package/tech/linting/index.d.ts +129 -7
  157. package/tech/linting/index.d.ts.map +1 -1
  158. package/tech/linting/index.esm.js +118 -503
  159. package/tech/monorepo/index.cjs.js +246 -573
  160. package/tech/monorepo/index.d.ts +241 -10
  161. package/tech/monorepo/index.d.ts.map +1 -1
  162. package/tech/monorepo/index.esm.js +226 -553
  163. package/tech/testing/index.cjs.js +216 -571
  164. package/tech/testing/index.d.ts +176 -8
  165. package/tech/testing/index.d.ts.map +1 -1
  166. package/tech/testing/index.esm.js +198 -553
  167. package/tech/types/index.cjs.js +123 -506
  168. package/tech/types/index.d.ts +120 -2
  169. package/tech/types/index.d.ts.map +1 -1
  170. package/tech/types/index.esm.js +101 -484
  171. package/vfs/index.cjs.js +647 -1142
  172. package/vfs/index.d.ts +360 -6
  173. package/vfs/index.d.ts.map +1 -1
  174. package/vfs/index.esm.js +672 -1167
  175. package/ARCHITECTURE.md +0 -370
  176. package/analyze.d.ts +0 -33
  177. package/analyze.d.ts.map +0 -1
  178. package/cli/commands/analyze.d.ts +0 -28
  179. package/cli/commands/analyze.d.ts.map +0 -1
  180. package/cli/commands/config.d.ts +0 -27
  181. package/cli/commands/config.d.ts.map +0 -1
  182. package/cli/commands/deps.d.ts +0 -24
  183. package/cli/commands/deps.d.ts.map +0 -1
  184. package/cli/commands/tree.d.ts +0 -36
  185. package/cli/commands/tree.d.ts.map +0 -1
  186. package/cli/index.cjs.js.map +0 -1
  187. package/cli/index.esm.js.map +0 -1
  188. package/cli/run.d.ts +0 -25
  189. package/cli/run.d.ts.map +0 -1
  190. package/cli/types.d.ts +0 -55
  191. package/cli/types.d.ts.map +0 -1
  192. package/core/cache.d.ts +0 -158
  193. package/core/cache.d.ts.map +0 -1
  194. package/core/encoding/convert.d.ts +0 -32
  195. package/core/encoding/convert.d.ts.map +0 -1
  196. package/core/encoding/detect.d.ts +0 -91
  197. package/core/encoding/detect.d.ts.map +0 -1
  198. package/core/encoding/index.cjs.js.map +0 -1
  199. package/core/encoding/index.esm.js.map +0 -1
  200. package/core/errors/structured-errors.d.ts +0 -66
  201. package/core/errors/structured-errors.d.ts.map +0 -1
  202. package/core/fs/directory.d.ts +0 -91
  203. package/core/fs/directory.d.ts.map +0 -1
  204. package/core/fs/index.cjs.js.map +0 -1
  205. package/core/fs/index.esm.js.map +0 -1
  206. package/core/fs/read.d.ts +0 -94
  207. package/core/fs/read.d.ts.map +0 -1
  208. package/core/fs/stat.d.ts +0 -58
  209. package/core/fs/stat.d.ts.map +0 -1
  210. package/core/fs/traversal.d.ts +0 -26
  211. package/core/fs/traversal.d.ts.map +0 -1
  212. package/core/fs/write.d.ts +0 -75
  213. package/core/fs/write.d.ts.map +0 -1
  214. package/core/index.cjs.js.map +0 -1
  215. package/core/index.esm.js.map +0 -1
  216. package/core/logger.d.ts +0 -111
  217. package/core/logger.d.ts.map +0 -1
  218. package/core/path/index.cjs.js.map +0 -1
  219. package/core/path/index.esm.js.map +0 -1
  220. package/core/path/join.d.ts +0 -17
  221. package/core/path/join.d.ts.map +0 -1
  222. package/core/path/normalize.d.ts +0 -37
  223. package/core/path/normalize.d.ts.map +0 -1
  224. package/core/path/resolve.d.ts +0 -52
  225. package/core/path/resolve.d.ts.map +0 -1
  226. package/core/path/segments.d.ts +0 -59
  227. package/core/path/segments.d.ts.map +0 -1
  228. package/core/patterns/glob.d.ts +0 -42
  229. package/core/patterns/glob.d.ts.map +0 -1
  230. package/core/platform/detect.d.ts +0 -66
  231. package/core/platform/detect.d.ts.map +0 -1
  232. package/core/platform/index.cjs.js.map +0 -1
  233. package/core/platform/index.esm.js.map +0 -1
  234. package/core/platform/line-endings.d.ts +0 -48
  235. package/core/platform/line-endings.d.ts.map +0 -1
  236. package/heuristics/dependencies/analyze.d.ts +0 -77
  237. package/heuristics/dependencies/analyze.d.ts.map +0 -1
  238. package/heuristics/dependencies/index.cjs.js.map +0 -1
  239. package/heuristics/dependencies/index.esm.js.map +0 -1
  240. package/heuristics/entry-points/discover.d.ts +0 -113
  241. package/heuristics/entry-points/discover.d.ts.map +0 -1
  242. package/heuristics/entry-points/index.cjs.js.map +0 -1
  243. package/heuristics/entry-points/index.esm.js.map +0 -1
  244. package/heuristics/framework/identify.d.ts +0 -84
  245. package/heuristics/framework/identify.d.ts.map +0 -1
  246. package/heuristics/framework/index.cjs.js.map +0 -1
  247. package/heuristics/framework/index.esm.js.map +0 -1
  248. package/heuristics/index.cjs.js.map +0 -1
  249. package/heuristics/index.esm.js.map +0 -1
  250. package/heuristics/project-type/detect.d.ts +0 -61
  251. package/heuristics/project-type/detect.d.ts.map +0 -1
  252. package/heuristics/project-type/index.cjs.js.map +0 -1
  253. package/heuristics/project-type/index.esm.js.map +0 -1
  254. package/index.cjs.js.map +0 -1
  255. package/index.esm.js.map +0 -1
  256. package/models/index.cjs.js.map +0 -1
  257. package/models/index.esm.js.map +0 -1
  258. package/nx/detect.d.ts +0 -105
  259. package/nx/detect.d.ts.map +0 -1
  260. package/nx/devkit-loader.d.ts +0 -62
  261. package/nx/devkit-loader.d.ts.map +0 -1
  262. package/nx/index.cjs.js.map +0 -1
  263. package/nx/index.esm.js.map +0 -1
  264. package/nx/project-config.d.ts +0 -111
  265. package/nx/project-config.d.ts.map +0 -1
  266. package/project/config/detect.d.ts +0 -77
  267. package/project/config/detect.d.ts.map +0 -1
  268. package/project/config/index.cjs.js.map +0 -1
  269. package/project/config/index.esm.js.map +0 -1
  270. package/project/config/parse.d.ts +0 -53
  271. package/project/config/parse.d.ts.map +0 -1
  272. package/project/config/patterns.d.ts +0 -31
  273. package/project/config/patterns.d.ts.map +0 -1
  274. package/project/index.cjs.js.map +0 -1
  275. package/project/index.esm.js.map +0 -1
  276. package/project/package/dependencies.d.ts +0 -101
  277. package/project/package/dependencies.d.ts.map +0 -1
  278. package/project/package/index.cjs.js.map +0 -1
  279. package/project/package/index.esm.js.map +0 -1
  280. package/project/package/read.d.ts +0 -67
  281. package/project/package/read.d.ts.map +0 -1
  282. package/project/root/detect.d.ts +0 -65
  283. package/project/root/detect.d.ts.map +0 -1
  284. package/project/root/index.cjs.js.map +0 -1
  285. package/project/root/index.esm.js.map +0 -1
  286. package/project/traversal/index.cjs.js.map +0 -1
  287. package/project/traversal/index.esm.js.map +0 -1
  288. package/project/traversal/search.d.ts +0 -59
  289. package/project/traversal/search.d.ts.map +0 -1
  290. package/project/traversal/walk.d.ts +0 -63
  291. package/project/traversal/walk.d.ts.map +0 -1
  292. package/tech/backend/detect-all.d.ts +0 -13
  293. package/tech/backend/detect-all.d.ts.map +0 -1
  294. package/tech/backend/express.d.ts +0 -11
  295. package/tech/backend/express.d.ts.map +0 -1
  296. package/tech/backend/fastify.d.ts +0 -11
  297. package/tech/backend/fastify.d.ts.map +0 -1
  298. package/tech/backend/hono.d.ts +0 -11
  299. package/tech/backend/hono.d.ts.map +0 -1
  300. package/tech/backend/index.cjs.js.map +0 -1
  301. package/tech/backend/index.esm.js.map +0 -1
  302. package/tech/backend/koa.d.ts +0 -11
  303. package/tech/backend/koa.d.ts.map +0 -1
  304. package/tech/backend/nestjs.d.ts +0 -11
  305. package/tech/backend/nestjs.d.ts.map +0 -1
  306. package/tech/backend/types.d.ts +0 -31
  307. package/tech/backend/types.d.ts.map +0 -1
  308. package/tech/build/babel.d.ts +0 -13
  309. package/tech/build/babel.d.ts.map +0 -1
  310. package/tech/build/detect-all.d.ts +0 -13
  311. package/tech/build/detect-all.d.ts.map +0 -1
  312. package/tech/build/esbuild.d.ts +0 -11
  313. package/tech/build/esbuild.d.ts.map +0 -1
  314. package/tech/build/index.cjs.js.map +0 -1
  315. package/tech/build/index.esm.js.map +0 -1
  316. package/tech/build/parcel.d.ts +0 -13
  317. package/tech/build/parcel.d.ts.map +0 -1
  318. package/tech/build/rollup.d.ts +0 -13
  319. package/tech/build/rollup.d.ts.map +0 -1
  320. package/tech/build/swc.d.ts +0 -13
  321. package/tech/build/swc.d.ts.map +0 -1
  322. package/tech/build/types.d.ts +0 -31
  323. package/tech/build/types.d.ts.map +0 -1
  324. package/tech/build/vite.d.ts +0 -13
  325. package/tech/build/vite.d.ts.map +0 -1
  326. package/tech/build/webpack.d.ts +0 -13
  327. package/tech/build/webpack.d.ts.map +0 -1
  328. package/tech/frontend/angular.d.ts +0 -11
  329. package/tech/frontend/angular.d.ts.map +0 -1
  330. package/tech/frontend/astro.d.ts +0 -11
  331. package/tech/frontend/astro.d.ts.map +0 -1
  332. package/tech/frontend/detect-all.d.ts +0 -13
  333. package/tech/frontend/detect-all.d.ts.map +0 -1
  334. package/tech/frontend/gatsby.d.ts +0 -11
  335. package/tech/frontend/gatsby.d.ts.map +0 -1
  336. package/tech/frontend/index.cjs.js.map +0 -1
  337. package/tech/frontend/index.esm.js.map +0 -1
  338. package/tech/frontend/nextjs.d.ts +0 -11
  339. package/tech/frontend/nextjs.d.ts.map +0 -1
  340. package/tech/frontend/nuxt.d.ts +0 -11
  341. package/tech/frontend/nuxt.d.ts.map +0 -1
  342. package/tech/frontend/qwik.d.ts +0 -11
  343. package/tech/frontend/qwik.d.ts.map +0 -1
  344. package/tech/frontend/react.d.ts +0 -11
  345. package/tech/frontend/react.d.ts.map +0 -1
  346. package/tech/frontend/remix.d.ts +0 -11
  347. package/tech/frontend/remix.d.ts.map +0 -1
  348. package/tech/frontend/solid.d.ts +0 -11
  349. package/tech/frontend/solid.d.ts.map +0 -1
  350. package/tech/frontend/svelte.d.ts +0 -11
  351. package/tech/frontend/svelte.d.ts.map +0 -1
  352. package/tech/frontend/sveltekit.d.ts +0 -11
  353. package/tech/frontend/sveltekit.d.ts.map +0 -1
  354. package/tech/frontend/types.d.ts +0 -35
  355. package/tech/frontend/types.d.ts.map +0 -1
  356. package/tech/frontend/vue.d.ts +0 -11
  357. package/tech/frontend/vue.d.ts.map +0 -1
  358. package/tech/index.cjs.js.map +0 -1
  359. package/tech/index.esm.js.map +0 -1
  360. package/tech/legacy/angularjs.d.ts +0 -12
  361. package/tech/legacy/angularjs.d.ts.map +0 -1
  362. package/tech/legacy/backbone.d.ts +0 -11
  363. package/tech/legacy/backbone.d.ts.map +0 -1
  364. package/tech/legacy/detect-all.d.ts +0 -13
  365. package/tech/legacy/detect-all.d.ts.map +0 -1
  366. package/tech/legacy/ember.d.ts +0 -11
  367. package/tech/legacy/ember.d.ts.map +0 -1
  368. package/tech/legacy/index.cjs.js.map +0 -1
  369. package/tech/legacy/index.esm.js.map +0 -1
  370. package/tech/legacy/jquery.d.ts +0 -11
  371. package/tech/legacy/jquery.d.ts.map +0 -1
  372. package/tech/legacy/types.d.ts +0 -33
  373. package/tech/legacy/types.d.ts.map +0 -1
  374. package/tech/linting/biome.d.ts +0 -11
  375. package/tech/linting/biome.d.ts.map +0 -1
  376. package/tech/linting/detect-all.d.ts +0 -13
  377. package/tech/linting/detect-all.d.ts.map +0 -1
  378. package/tech/linting/eslint.d.ts +0 -13
  379. package/tech/linting/eslint.d.ts.map +0 -1
  380. package/tech/linting/index.cjs.js.map +0 -1
  381. package/tech/linting/index.esm.js.map +0 -1
  382. package/tech/linting/prettier.d.ts +0 -13
  383. package/tech/linting/prettier.d.ts.map +0 -1
  384. package/tech/linting/stylelint.d.ts +0 -13
  385. package/tech/linting/stylelint.d.ts.map +0 -1
  386. package/tech/linting/types.d.ts +0 -31
  387. package/tech/linting/types.d.ts.map +0 -1
  388. package/tech/monorepo/detect-all.d.ts +0 -13
  389. package/tech/monorepo/detect-all.d.ts.map +0 -1
  390. package/tech/monorepo/index.cjs.js.map +0 -1
  391. package/tech/monorepo/index.esm.js.map +0 -1
  392. package/tech/monorepo/lerna.d.ts +0 -11
  393. package/tech/monorepo/lerna.d.ts.map +0 -1
  394. package/tech/monorepo/npm-workspaces.d.ts +0 -11
  395. package/tech/monorepo/npm-workspaces.d.ts.map +0 -1
  396. package/tech/monorepo/nx.d.ts +0 -11
  397. package/tech/monorepo/nx.d.ts.map +0 -1
  398. package/tech/monorepo/pnpm-workspaces.d.ts +0 -9
  399. package/tech/monorepo/pnpm-workspaces.d.ts.map +0 -1
  400. package/tech/monorepo/rush.d.ts +0 -11
  401. package/tech/monorepo/rush.d.ts.map +0 -1
  402. package/tech/monorepo/turborepo.d.ts +0 -11
  403. package/tech/monorepo/turborepo.d.ts.map +0 -1
  404. package/tech/monorepo/types.d.ts +0 -39
  405. package/tech/monorepo/types.d.ts.map +0 -1
  406. package/tech/monorepo/yarn-workspaces.d.ts +0 -11
  407. package/tech/monorepo/yarn-workspaces.d.ts.map +0 -1
  408. package/tech/shared-utils/detector-helpers.d.ts +0 -52
  409. package/tech/shared-utils/detector-helpers.d.ts.map +0 -1
  410. package/tech/shared-utils/types.d.ts +0 -41
  411. package/tech/shared-utils/types.d.ts.map +0 -1
  412. package/tech/testing/cypress.d.ts +0 -13
  413. package/tech/testing/cypress.d.ts.map +0 -1
  414. package/tech/testing/detect-all.d.ts +0 -13
  415. package/tech/testing/detect-all.d.ts.map +0 -1
  416. package/tech/testing/index.cjs.js.map +0 -1
  417. package/tech/testing/index.esm.js.map +0 -1
  418. package/tech/testing/jest.d.ts +0 -13
  419. package/tech/testing/jest.d.ts.map +0 -1
  420. package/tech/testing/mocha.d.ts +0 -13
  421. package/tech/testing/mocha.d.ts.map +0 -1
  422. package/tech/testing/playwright.d.ts +0 -13
  423. package/tech/testing/playwright.d.ts.map +0 -1
  424. package/tech/testing/types.d.ts +0 -35
  425. package/tech/testing/types.d.ts.map +0 -1
  426. package/tech/testing/vitest.d.ts +0 -13
  427. package/tech/testing/vitest.d.ts.map +0 -1
  428. package/tech/types/detectors.d.ts +0 -67
  429. package/tech/types/detectors.d.ts.map +0 -1
  430. package/tech/types/index.cjs.js.map +0 -1
  431. package/tech/types/index.esm.js.map +0 -1
  432. package/vfs/commit.d.ts +0 -32
  433. package/vfs/commit.d.ts.map +0 -1
  434. package/vfs/diff.d.ts +0 -73
  435. package/vfs/diff.d.ts.map +0 -1
  436. package/vfs/factory.d.ts +0 -37
  437. package/vfs/factory.d.ts.map +0 -1
  438. package/vfs/fs-tree.d.ts +0 -13
  439. package/vfs/fs-tree.d.ts.map +0 -1
  440. package/vfs/index.cjs.js.map +0 -1
  441. package/vfs/index.esm.js.map +0 -1
  442. package/vfs/types.d.ts +0 -179
  443. package/vfs/types.d.ts.map +0 -1
@@ -1,423 +1,20 @@
1
1
  import { join as join$1 } from 'node:path';
2
+ import { stringify, parse } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/json/index.esm.js';
3
+ import { min, round } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.esm.js';
4
+ import { freeze, entries, keys, defineProperties, values } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.esm.js';
2
5
  import { existsSync, readFileSync, statSync, lstatSync, readdirSync } from 'node:fs';
3
-
4
- /**
5
- * Safe copies of JSON built-in methods.
6
- *
7
- * These references are captured at module initialization time to protect against
8
- * prototype pollution attacks. Import only what you need for tree-shaking.
9
- *
10
- * @module @hyperfrontend/immutable-api-utils/built-in-copy/json
11
- */
12
- const _JSON = globalThis.JSON;
13
- /**
14
- * (Safe copy) Converts a JavaScript Object Notation (JSON) string into an object.
15
- */
16
- const parse = _JSON.parse;
17
- /**
18
- * (Safe copy) Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
19
- */
20
- const stringify = _JSON.stringify;
21
-
22
- /**
23
- * Safe copies of Math built-in methods.
24
- *
25
- * These references are captured at module initialization time to protect against
26
- * prototype pollution attacks. Import only what you need for tree-shaking.
27
- *
28
- * @module @hyperfrontend/immutable-api-utils/built-in-copy/math
29
- */
30
- const _Math = globalThis.Math;
31
- /**
32
- * (Safe copy) Returns the value of a number rounded to the nearest integer.
33
- */
34
- const round = _Math.round;
35
- /**
36
- * (Safe copy) Returns the smaller of zero or more numbers.
37
- */
38
- const min = _Math.min;
39
-
40
- /**
41
- * Safe copies of Object built-in methods.
42
- *
43
- * These references are captured at module initialization time to protect against
44
- * prototype pollution attacks. Import only what you need for tree-shaking.
45
- *
46
- * @module @hyperfrontend/immutable-api-utils/built-in-copy/object
47
- */
48
- const _Object = globalThis.Object;
49
- /**
50
- * (Safe copy) Prevents modification of existing property attributes and values,
51
- * and prevents the addition of new properties.
52
- */
53
- const freeze = _Object.freeze;
54
- /**
55
- * (Safe copy) Returns the names of the enumerable string properties and methods of an object.
56
- */
57
- const keys = _Object.keys;
58
- /**
59
- * (Safe copy) Returns an array of key/values of the enumerable own properties of an object.
60
- */
61
- const entries = _Object.entries;
62
- /**
63
- * (Safe copy) Returns an array of values of the enumerable own properties of an object.
64
- */
65
- const values = _Object.values;
66
- /**
67
- * (Safe copy) Adds one or more properties to an object, and/or modifies attributes of existing properties.
68
- */
69
- const defineProperties = _Object.defineProperties;
70
-
71
- /**
72
- * Safe copies of Error built-ins via factory functions.
73
- *
74
- * Since constructors cannot be safely captured via Object.assign, this module
75
- * provides factory functions that use Reflect.construct internally.
76
- *
77
- * These references are captured at module initialization time to protect against
78
- * prototype pollution attacks. Import only what you need for tree-shaking.
79
- *
80
- * @module @hyperfrontend/immutable-api-utils/built-in-copy/error
81
- */
82
- const _Error = globalThis.Error;
83
- const _Reflect$2 = globalThis.Reflect;
84
- /**
85
- * (Safe copy) Creates a new Error using the captured Error constructor.
86
- * Use this instead of `new Error()`.
87
- *
88
- * @param message - Optional error message.
89
- * @param options - Optional error options.
90
- * @returns A new Error instance.
91
- */
92
- const createError = (message, options) => _Reflect$2.construct(_Error, [message, options]);
93
-
94
- /**
95
- * Safe copies of Array built-in static methods.
96
- *
97
- * These references are captured at module initialization time to protect against
98
- * prototype pollution attacks. Import only what you need for tree-shaking.
99
- *
100
- * @module @hyperfrontend/immutable-api-utils/built-in-copy/array
101
- */
102
- const _Array = globalThis.Array;
103
- /**
104
- * (Safe copy) Determines whether the passed value is an Array.
105
- */
106
- const isArray = _Array.isArray;
107
-
108
- /**
109
- * Safe copies of Console built-in methods.
110
- *
111
- * These references are captured at module initialization time to protect against
112
- * prototype pollution attacks. Import only what you need for tree-shaking.
113
- *
114
- * @module @hyperfrontend/immutable-api-utils/built-in-copy/console
115
- */
116
- const _console = globalThis.console;
117
- /**
118
- * (Safe copy) Outputs a message to the console.
119
- */
120
- const log = _console.log.bind(_console);
121
- /**
122
- * (Safe copy) Outputs a warning message to the console.
123
- */
124
- const warn = _console.warn.bind(_console);
125
- /**
126
- * (Safe copy) Outputs an error message to the console.
127
- */
128
- const error = _console.error.bind(_console);
129
- /**
130
- * (Safe copy) Outputs an informational message to the console.
131
- */
132
- const info = _console.info.bind(_console);
133
- /**
134
- * (Safe copy) Outputs a debug message to the console.
135
- */
136
- const debug = _console.debug.bind(_console);
137
- /**
138
- * (Safe copy) Outputs a stack trace to the console.
139
- */
140
- _console.trace.bind(_console);
141
- /**
142
- * (Safe copy) Displays an interactive listing of the properties of a specified object.
143
- */
144
- _console.dir.bind(_console);
145
- /**
146
- * (Safe copy) Displays tabular data as a table.
147
- */
148
- _console.table.bind(_console);
149
- /**
150
- * (Safe copy) Writes an error message to the console if the assertion is false.
151
- */
152
- _console.assert.bind(_console);
153
- /**
154
- * (Safe copy) Clears the console.
155
- */
156
- _console.clear.bind(_console);
157
- /**
158
- * (Safe copy) Logs the number of times that this particular call to count() has been called.
159
- */
160
- _console.count.bind(_console);
161
- /**
162
- * (Safe copy) Resets the counter used with console.count().
163
- */
164
- _console.countReset.bind(_console);
165
- /**
166
- * (Safe copy) Creates a new inline group in the console.
167
- */
168
- _console.group.bind(_console);
169
- /**
170
- * (Safe copy) Creates a new inline group in the console that is initially collapsed.
171
- */
172
- _console.groupCollapsed.bind(_console);
173
- /**
174
- * (Safe copy) Exits the current inline group.
175
- */
176
- _console.groupEnd.bind(_console);
177
- /**
178
- * (Safe copy) Starts a timer with a name specified as an input parameter.
179
- */
180
- _console.time.bind(_console);
181
- /**
182
- * (Safe copy) Stops a timer that was previously started.
183
- */
184
- _console.timeEnd.bind(_console);
185
- /**
186
- * (Safe copy) Logs the current value of a timer that was previously started.
187
- */
188
- _console.timeLog.bind(_console);
189
-
190
- /**
191
- * Safe copies of Set built-in via factory function.
192
- *
193
- * Since constructors cannot be safely captured via Object.assign, this module
194
- * provides a factory function that uses Reflect.construct internally.
195
- *
196
- * These references are captured at module initialization time to protect against
197
- * prototype pollution attacks. Import only what you need for tree-shaking.
198
- *
199
- * @module @hyperfrontend/immutable-api-utils/built-in-copy/set
200
- */
201
- const _Set = globalThis.Set;
202
- const _Reflect$1 = globalThis.Reflect;
203
- /**
204
- * (Safe copy) Creates a new Set using the captured Set constructor.
205
- * Use this instead of `new Set()`.
206
- *
207
- * @param iterable - Optional iterable of values.
208
- * @returns A new Set instance.
209
- */
210
- const createSet = (iterable) => _Reflect$1.construct(_Set, iterable ? [iterable] : []);
211
-
212
- const registeredClasses = [];
213
-
214
- /**
215
- * Returns the data type of the target.
216
- * Uses native `typeof` operator, however, makes distinction between `null`, `array`, and `object`.
217
- * Also, when classes are registered via `registerClass`, it checks if objects are instance of any known registered class.
218
- *
219
- * @param target - The target to get the data type of.
220
- * @returns The data type of the target.
221
- */
222
- const getType = (target) => {
223
- if (target === null)
224
- return 'null';
225
- const nativeDataType = typeof target;
226
- if (nativeDataType === 'object') {
227
- if (isArray(target))
228
- return 'array';
229
- for (const registeredClass of registeredClasses) {
230
- if (target instanceof registeredClass)
231
- return registeredClass.name;
232
- }
233
- }
234
- return nativeDataType;
235
- };
236
-
237
- /**
238
- * Safe copies of Map built-in via factory function.
239
- *
240
- * Since constructors cannot be safely captured via Object.assign, this module
241
- * provides a factory function that uses Reflect.construct internally.
242
- *
243
- * These references are captured at module initialization time to protect against
244
- * prototype pollution attacks. Import only what you need for tree-shaking.
245
- *
246
- * @module @hyperfrontend/immutable-api-utils/built-in-copy/map
247
- */
248
- const _Map = globalThis.Map;
249
- const _Reflect = globalThis.Reflect;
250
- /**
251
- * (Safe copy) Creates a new Map using the captured Map constructor.
252
- * Use this instead of `new Map()`.
253
- *
254
- * @param iterable - Optional iterable of key-value pairs.
255
- * @returns A new Map instance.
256
- */
257
- const createMap = (iterable) => _Reflect.construct(_Map, iterable ? [iterable] : []);
258
-
259
- /* eslint-disable @typescript-eslint/no-explicit-any */
260
- /**
261
- * Creates a wrapper function that only executes the wrapped function if the condition function returns true.
262
- *
263
- * @param func - The function to be conditionally executed.
264
- * @param conditionFunc - A function that returns a boolean, determining if `func` should be executed.
265
- * @returns A wrapped version of `func` that executes conditionally.
266
- */
267
- function createConditionalExecutionFunction(func, conditionFunc) {
268
- return function (...args) {
269
- if (conditionFunc()) {
270
- return func(...args);
271
- }
272
- };
273
- }
274
-
275
- /* eslint-disable @typescript-eslint/no-explicit-any */
276
- /**
277
- * Creates a wrapper function that silently ignores any errors thrown by the wrapped void function.
278
- * This function is specifically for wrapping functions that do not return a value (void functions).
279
- * Exceptions are swallowed without any logging or handling.
280
- *
281
- * @param func - The void function to be wrapped.
282
- * @returns A wrapped version of the input function that ignores errors.
283
- */
284
- function createErrorIgnoringFunction(func) {
285
- return function (...args) {
286
- try {
287
- func(...args);
288
- }
289
- catch {
290
- // Deliberately swallowing/ignoring the exception
291
- }
292
- };
293
- }
294
-
295
- /* eslint-disable @typescript-eslint/no-unused-vars */
296
- /**
297
- * A no-operation function (noop) that does nothing regardless of the arguments passed.
298
- * It is designed to be as permissive as possible in its typing without using the `Function` keyword.
299
- *
300
- * @param args - Any arguments passed to the function (ignored)
301
- */
302
- const noop = (...args) => {
303
- // Intentionally does nothing
304
- };
305
-
306
- const logLevels = ['none', 'error', 'warn', 'log', 'info', 'debug'];
307
- const priority = {
308
- error: 4,
309
- warn: 3,
310
- log: 2,
311
- info: 1,
312
- debug: 0,
313
- };
314
- /**
315
- * Validates whether a given string is a valid log level.
316
- *
317
- * @param level - The log level to validate
318
- * @returns True if the level is valid, false otherwise
319
- */
320
- function isValidLogLevel(level) {
321
- return logLevels.includes(level);
322
- }
323
- /**
324
- * Creates a log level configuration manager for controlling logging behavior.
325
- * Provides methods to get, set, and evaluate log levels based on priority.
326
- *
327
- * @param level - The initial log level (defaults to 'error')
328
- * @returns A configuration object with log level management methods
329
- * @throws {Error} When the provided level is not a valid log level
330
- */
331
- function createLogLevelConfig(level = 'error') {
332
- if (!isValidLogLevel(level)) {
333
- throw createError('Cannot create log level configuration with a valid default log level');
334
- }
335
- const state = { level };
336
- const getLogLevel = () => state.level;
337
- const setLogLevel = (level) => {
338
- if (!isValidLogLevel(level)) {
339
- throw createError(`Cannot set value '${level}' level. Expected levels are ${logLevels}.`);
340
- }
341
- state.level = level;
342
- };
343
- const shouldLog = (level) => {
344
- if (state.level === 'none' || level === 'none' || !isValidLogLevel(level)) {
345
- return false;
346
- }
347
- return priority[level] >= priority[state.level];
348
- };
349
- return freeze({
350
- getLogLevel,
351
- setLogLevel,
352
- shouldLog,
353
- });
354
- }
355
-
356
- /**
357
- * Creates a logger instance with configurable log level filtering.
358
- * Each log function is wrapped to respect the current log level setting.
359
- *
360
- * @param error - Function to handle error-level logs (required)
361
- * @param warn - Function to handle warning-level logs (optional, defaults to noop)
362
- * @param log - Function to handle standard logs (optional, defaults to noop)
363
- * @param info - Function to handle info-level logs (optional, defaults to noop)
364
- * @param debug - Function to handle debug-level logs (optional, defaults to noop)
365
- * @returns A frozen logger object with log methods and level control
366
- * @throws {ErrorLevelFn} When any provided log function is invalid
367
- */
368
- function createLogger(error, warn = noop, log = noop, info = noop, debug = noop) {
369
- if (notValidLogFn(error)) {
370
- throw createError(notFnMsg('error'));
371
- }
372
- if (notValidLogFn(warn)) {
373
- throw createError(notFnMsg('warn'));
374
- }
375
- if (notValidLogFn(log)) {
376
- throw createError(notFnMsg('log'));
377
- }
378
- if (notValidLogFn(info)) {
379
- throw createError(notFnMsg('info'));
380
- }
381
- if (notValidLogFn(debug)) {
382
- throw createError(notFnMsg('debug'));
383
- }
384
- const { setLogLevel, getLogLevel, shouldLog } = createLogLevelConfig();
385
- const wrapLogFn = (fn, level) => {
386
- if (fn === noop)
387
- return fn;
388
- const condition = () => shouldLog(level);
389
- return createConditionalExecutionFunction(createErrorIgnoringFunction(fn), condition);
390
- };
391
- return freeze({
392
- error: wrapLogFn(error, 'error'),
393
- warn: wrapLogFn(warn, 'warn'),
394
- log: wrapLogFn(log, 'log'),
395
- info: wrapLogFn(info, 'info'),
396
- debug: wrapLogFn(debug, 'debug'),
397
- setLogLevel,
398
- getLogLevel,
399
- });
400
- }
401
- /**
402
- * Validates whether a given value is a valid log function.
403
- *
404
- * @param fn - The value to validate
405
- * @returns True if the value is not a function (invalid), false if it is valid
406
- */
407
- function notValidLogFn(fn) {
408
- return getType(fn) !== 'function' && fn !== noop;
409
- }
410
- /**
411
- * Generates an error message for invalid log function parameters.
412
- *
413
- * @param label - The name of the log function that failed validation
414
- * @returns A formatted error message string
415
- */
416
- function notFnMsg(label) {
417
- return `Cannot create a logger when ${label} is not a function`;
418
- }
419
-
420
- createLogger(error, warn, log, info, debug);
6
+ import { isArray } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.esm.js';
7
+ import { error, warn, log, info, debug } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/console/index.esm.js';
8
+ import { createSet } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/set/index.esm.js';
9
+ import { createLogger } from '../../_dependencies/@hyperfrontend/logging/index.esm.js';
10
+ import { createError } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.esm.js';
11
+ import { createMap } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/map/index.esm.js';
12
+ import { isSafePath } from '../../_shared/core/fs/guard/index.esm.js';
13
+ import { isDirectory, exists } from '../../_shared/core/fs/stat/index.esm.js';
14
+ import { join } from '../../_shared/core/path/join/index.esm.js';
15
+ import { createCache } from '../../_shared/core/cache/index.esm.js';
16
+ import { collectAllDependencies, parseVersionString, locateConfigFile, filterScriptsByCommand } from '../../_shared/tech/shared-utils/detector-helpers/index.esm.js';
17
+ import { pnpmWorkspacesDetector } from '../../_shared/tech/monorepo/pnpm-workspaces/index.esm.js';
421
18
 
422
19
  /**
423
20
  * Global log level registry.
@@ -457,6 +54,13 @@ function isSensitiveKey(key) {
457
54
  *
458
55
  * @param obj - Object to sanitize
459
56
  * @returns New object with sensitive values redacted
57
+ *
58
+ * @example Sanitizing sensitive data
59
+ * ```typescript
60
+ * const config = { apiKey: 'secret123', endpoint: 'https://api.example.com' }
61
+ * const safe = sanitize(config)
62
+ * // => { apiKey: '[REDACTED]', endpoint: 'https://api.example.com' }
63
+ * ```
460
64
  */
461
65
  function sanitize(obj) {
462
66
  if (obj === null || obj === undefined) {
@@ -507,7 +111,7 @@ function formatMessage(namespace, message, meta) {
507
111
  * @param options - Logger configuration options
508
112
  * @returns A configured scoped logger instance
509
113
  *
510
- * @example
114
+ * @example Creating a scoped logger
511
115
  * ```typescript
512
116
  * const logger = createScopedLogger('project-scope')
513
117
  * logger.setLogLevel('debug')
@@ -544,7 +148,7 @@ function createScopedLogger(namespace, options = {}) {
544
148
  * Default logger instance for the project-scope library.
545
149
  * Use this for general logging within the library.
546
150
  *
547
- * @example
151
+ * @example Using the default logger
548
152
  * ```typescript
549
153
  * import { logger } from '@hyperfrontend/project-scope/core'
550
154
  *
@@ -562,6 +166,15 @@ createScopedLogger('project-scope:fs');
562
166
  * @param code - The category code for this type of filesystem failure
563
167
  * @param context - Additional context including path, operation, and cause
564
168
  * @returns A configured Error object with code and context properties
169
+ *
170
+ * @example Creating a file system error
171
+ * ```typescript
172
+ * throw createFileSystemError(
173
+ * 'Cannot read file',
174
+ * 'FS_READ_ERROR',
175
+ * { path: './missing.txt', operation: 'read' }
176
+ * )
177
+ * ```
565
178
  */
566
179
  function createFileSystemError(message, code, context) {
567
180
  const error = createError(message);
@@ -577,9 +190,17 @@ function createFileSystemError(message, code, context) {
577
190
  * @param filePath - Path to file
578
191
  * @param encoding - File encoding (default: utf-8)
579
192
  * @returns File contents or null if file doesn't exist
193
+ *
194
+ * @example Reading file if it exists
195
+ * ```typescript
196
+ * const content = readFileIfExists('./optional-config.json')
197
+ * if (content) {
198
+ * // File existed, use content
199
+ * }
200
+ * ```
580
201
  */
581
202
  function readFileIfExists(filePath, encoding = 'utf-8') {
582
- if (!existsSync(filePath)) {
203
+ if (!isSafePath(filePath) || !existsSync(filePath)) {
583
204
  return null;
584
205
  }
585
206
  try {
@@ -590,56 +211,6 @@ function readFileIfExists(filePath, encoding = 'utf-8') {
590
211
  }
591
212
  }
592
213
 
593
- createScopedLogger('project-scope:fs:write');
594
-
595
- /**
596
- * Get file stats with error handling.
597
- *
598
- * @param filePath - Path to file
599
- * @param followSymlinks - Whether to follow symlinks (default: true)
600
- * @returns File stats or null if path doesn't exist
601
- */
602
- function getFileStat(filePath, followSymlinks = true) {
603
- if (!existsSync(filePath)) {
604
- return null;
605
- }
606
- try {
607
- const stat = followSymlinks ? statSync(filePath) : lstatSync(filePath);
608
- return {
609
- isFile: stat.isFile(),
610
- isDirectory: stat.isDirectory(),
611
- isSymlink: stat.isSymbolicLink(),
612
- size: stat.size,
613
- created: stat.birthtime,
614
- modified: stat.mtime,
615
- accessed: stat.atime,
616
- mode: stat.mode,
617
- };
618
- }
619
- catch {
620
- return null;
621
- }
622
- }
623
- /**
624
- * Check if path is a directory.
625
- *
626
- * @param dirPath - Path to check
627
- * @returns True if path is a directory
628
- */
629
- function isDirectory(dirPath) {
630
- const stats = getFileStat(dirPath);
631
- return stats?.isDirectory ?? false;
632
- }
633
- /**
634
- * Check if path exists.
635
- *
636
- * @param filePath - Path to check
637
- * @returns True if path exists
638
- */
639
- function exists(filePath) {
640
- return existsSync(filePath);
641
- }
642
-
643
214
  const fsDirLogger = createScopedLogger('project-scope:fs:dir');
644
215
  /**
645
216
  * List immediate contents of a directory.
@@ -648,7 +219,7 @@ const fsDirLogger = createScopedLogger('project-scope:fs:dir');
648
219
  * @returns Array of entries with metadata for each file/directory
649
220
  * @throws {Error} If directory doesn't exist or isn't a directory
650
221
  *
651
- * @example
222
+ * @example Listing directory contents
652
223
  * ```typescript
653
224
  * import { readDirectory } from '@hyperfrontend/project-scope'
654
225
  *
@@ -689,19 +260,10 @@ function readDirectory(dirPath) {
689
260
  }
690
261
  }
691
262
 
692
- /**
693
- * Join path segments.
694
- * Uses platform-specific separators (e.g., / or \).
695
- *
696
- * @param paths - Path segments to join
697
- * @returns Joined path
698
- */
699
- function join(...paths) {
700
- return join$1(...paths);
701
- }
702
-
703
263
  createScopedLogger('project-scope:fs:traversal');
704
264
 
265
+ createScopedLogger('project-scope:fs:write');
266
+
705
267
  const packageLogger = createScopedLogger('project-scope:project:package');
706
268
  /**
707
269
  * Verifies that a value is an object with only string values,
@@ -771,6 +333,16 @@ function validatePackageJson(data) {
771
333
  *
772
334
  * @param projectPath - Project directory path or path to package.json
773
335
  * @returns Parsed package.json or null if not found
336
+ *
337
+ * @example Reading package.json if it exists
338
+ * ```typescript
339
+ * import { readPackageJsonIfExists } from '@hyperfrontend/project-scope'
340
+ *
341
+ * const pkg = readPackageJsonIfExists('/path/to/project')
342
+ * if (pkg) {
343
+ * console.log('Found:', pkg.name)
344
+ * }
345
+ * ```
774
346
  */
775
347
  function readPackageJsonIfExists(projectPath) {
776
348
  const packageJsonPath = projectPath.endsWith('package.json') ? projectPath : join$1(projectPath, 'package.json');
@@ -791,222 +363,46 @@ function readPackageJsonIfExists(projectPath) {
791
363
  }
792
364
 
793
365
  /**
794
- * Global registry of all caches for bulk operations.
795
- */
796
- const cacheRegistry = createSet();
797
- /**
798
- * Create a cache with optional TTL and size limits.
799
- *
800
- * The cache provides a simple key-value store with:
801
- * - Optional TTL (time-to-live) for automatic expiration
802
- * - Optional maxSize for limiting cache size with FIFO eviction
803
- * - Lazy expiration (entries are checked on access)
804
- *
805
- * @param options - Cache configuration options
806
- * @returns Cache instance
366
+ * Detect Express in project.
807
367
  *
808
- * @example
368
+ * @param projectPath - Project directory path
369
+ * @param packageJson - Optional pre-loaded package.json
370
+ * @returns Detection result or null if not detected
371
+ * @example Detecting Express framework
809
372
  * ```typescript
810
- * // Basic cache
811
- * const cache = createCache<string, number>()
812
- * cache.set('answer', 42)
813
- * cache.get('answer') // 42
814
- *
815
- * // Cache with TTL (expires after 60 seconds)
816
- * const ttlCache = createCache<string, object>({ ttl: 60000 })
817
- *
818
- * // Cache with max size (evicts oldest when full)
819
- * const lruCache = createCache<string, object>({ maxSize: 100 })
373
+ * const pkg = {
374
+ * dependencies: { express: '^4.18.2', cors: '^2.8.5' },
375
+ * devDependencies: { '@types/express': '^4.17.17' },
376
+ * }
820
377
  *
821
- * // Combined options
822
- * const configCache = createCache<string, object>({
823
- * ttl: 30000,
824
- * maxSize: 50
825
- * })
378
+ * const result = expressDetector('/path/to/project', pkg)
379
+ * // => {
380
+ * // id: 'express',
381
+ * // name: 'Express',
382
+ * // version: '4.18.2',
383
+ * // confidence: 100,
384
+ * // detectedFrom: [
385
+ * // { type: 'package.json', field: 'dependencies.express' },
386
+ * // { type: 'package.json', field: 'dependencies.@types/express' },
387
+ * // { type: 'package.json', field: 'dependencies (express middleware)' },
388
+ * // ],
389
+ * // }
826
390
  * ```
827
391
  */
828
- function createCache(options) {
829
- const { ttl, maxSize } = options ?? {};
830
- const store = createMap();
831
- const insertionOrder = [];
832
- /**
833
- * Check if an entry is expired.
834
- *
835
- * @param entry - Cache entry to check
836
- * @returns True if entry is expired
837
- */
838
- function isExpired(entry) {
839
- if (ttl === undefined)
840
- return false;
841
- // eslint-disable-next-line workspace/no-unsafe-builtin-methods -- Date.now() is needed for Jest fake timers compatibility
842
- return Date.now() - entry.timestamp > ttl;
843
- }
844
- /**
845
- * Evict oldest entries to make room for new ones.
846
- */
847
- function evictIfNeeded() {
848
- if (maxSize === undefined)
849
- return;
850
- while (store.size >= maxSize && insertionOrder.length > 0) {
851
- const oldestKey = insertionOrder.shift();
852
- if (oldestKey !== undefined) {
853
- store.delete(oldestKey);
854
- }
855
- }
392
+ function expressDetector(projectPath, packageJson) {
393
+ const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
394
+ const sources = [];
395
+ let confidence = 0;
396
+ let version;
397
+ const deps = collectAllDependencies(pkg);
398
+ if (deps['express']) {
399
+ confidence += 80;
400
+ version = parseVersionString(deps['express']);
401
+ sources.push({ type: 'package.json', field: 'dependencies.express' });
856
402
  }
857
- /**
858
- * Remove key from insertion order tracking.
859
- *
860
- * @param key - Key to remove from order tracking
861
- */
862
- function removeFromOrder(key) {
863
- const index = insertionOrder.indexOf(key);
864
- if (index !== -1) {
865
- insertionOrder.splice(index, 1);
866
- }
867
- }
868
- const cache = {
869
- get(key) {
870
- const entry = store.get(key);
871
- if (!entry)
872
- return undefined;
873
- if (isExpired(entry)) {
874
- store.delete(key);
875
- removeFromOrder(key);
876
- return undefined;
877
- }
878
- return entry.value;
879
- },
880
- set(key, value) {
881
- if (store.has(key)) {
882
- removeFromOrder(key);
883
- }
884
- else {
885
- evictIfNeeded();
886
- }
887
- // eslint-disable-next-line workspace/no-unsafe-builtin-methods -- Date.now() is needed for Jest fake timers compatibility
888
- store.set(key, { value, timestamp: Date.now() });
889
- insertionOrder.push(key);
890
- },
891
- has(key) {
892
- const entry = store.get(key);
893
- if (!entry)
894
- return false;
895
- if (isExpired(entry)) {
896
- store.delete(key);
897
- removeFromOrder(key);
898
- return false;
899
- }
900
- return true;
901
- },
902
- delete(key) {
903
- removeFromOrder(key);
904
- return store.delete(key);
905
- },
906
- clear() {
907
- store.clear();
908
- insertionOrder.length = 0;
909
- },
910
- size() {
911
- return store.size;
912
- },
913
- keys() {
914
- return [...insertionOrder];
915
- },
916
- };
917
- cacheRegistry.add(cache);
918
- return freeze(cache);
919
- }
920
-
921
- /**
922
- * Get combined dependencies from package.json.
923
- * Merges dependencies, devDependencies, peerDependencies, and optionalDependencies.
924
- *
925
- * @param packageJson - The package.json object to extract dependencies from
926
- * @returns Combined dependencies as a single record
927
- */
928
- function collectAllDependencies(packageJson) {
929
- return {
930
- ...packageJson?.dependencies,
931
- ...packageJson?.devDependencies,
932
- ...packageJson?.peerDependencies,
933
- ...packageJson?.optionalDependencies,
934
- };
935
- }
936
- /**
937
- * Extract clean version from dependency version string.
938
- * Removes semver prefixes like ^, ~, >=, etc.
939
- * Uses character-by-character parsing to avoid ReDoS vulnerabilities.
940
- *
941
- * @param versionString - The version string with optional prefix characters
942
- * @returns The cleaned version string without prefix characters
943
- */
944
- function parseVersionString(versionString) {
945
- if (versionString === undefined || versionString === null)
946
- return undefined;
947
- let start = 0;
948
- while (start < versionString.length) {
949
- const char = versionString[start];
950
- if (char !== '^' && char !== '~' && char !== '>' && char !== '=' && char !== '<') {
951
- break;
952
- }
953
- start++;
954
- }
955
- return versionString.slice(start);
956
- }
957
- /**
958
- * Find first matching config file in project.
959
- * Note: Name avoids similarity to fs.readFile/fs.readFileSync.
960
- *
961
- * @param projectPath - The project directory path
962
- * @param patterns - Array of config file patterns to search for
963
- * @returns The first matching config file path or undefined
964
- */
965
- function locateConfigFile(projectPath, patterns) {
966
- for (const pattern of patterns) {
967
- const fullPath = join(projectPath, pattern);
968
- if (exists(fullPath)) {
969
- return pattern;
970
- }
971
- }
972
- return undefined;
973
- }
974
- /**
975
- * Find scripts containing a specific command.
976
- *
977
- * @param scripts - The scripts object from package.json
978
- * @param command - The command string to search for
979
- * @returns Array of script names that contain the command
980
- */
981
- function filterScriptsByCommand(scripts, command) {
982
- if (!scripts)
983
- return [];
984
- return entries(scripts)
985
- .filter(([, script]) => script.includes(command))
986
- .map(([name]) => name);
987
- }
988
-
989
- /**
990
- * Detect Express in project.
991
- *
992
- * @param projectPath - Project directory path
993
- * @param packageJson - Optional pre-loaded package.json
994
- * @returns Detection result or null if not detected
995
- */
996
- function expressDetector(projectPath, packageJson) {
997
- const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
998
- const sources = [];
999
- let confidence = 0;
1000
- let version;
1001
- const deps = collectAllDependencies(pkg);
1002
- if (deps['express']) {
1003
- confidence += 80;
1004
- version = parseVersionString(deps['express']);
1005
- sources.push({ type: 'package.json', field: 'dependencies.express' });
1006
- }
1007
- if (deps['@types/express']) {
1008
- confidence += 10;
1009
- sources.push({ type: 'package.json', field: 'dependencies.@types/express' });
403
+ if (deps['@types/express']) {
404
+ confidence += 10;
405
+ sources.push({ type: 'package.json', field: 'dependencies.@types/express' });
1010
406
  }
1011
407
  const expressMiddleware = keys(deps).filter((d) => d.includes('express-') || d === 'body-parser' || d === 'cors' || d === 'helmet' || d === 'morgan');
1012
408
  if (expressMiddleware.length > 0) {
@@ -1026,84 +422,109 @@ function expressDetector(projectPath, packageJson) {
1026
422
  }
1027
423
 
1028
424
  /**
1029
- * Detect NestJS in project.
425
+ * Detect Fastify in project.
1030
426
  *
1031
427
  * @param projectPath - Project directory path
1032
428
  * @param packageJson - Optional pre-loaded package.json
1033
429
  * @returns Detection result or null if not detected
430
+ * @example Detecting Fastify framework
431
+ * ```typescript
432
+ * const pkg = {
433
+ * dependencies: { fastify: '^4.24.0', '@fastify/cors': '^8.4.0' },
434
+ * }
435
+ *
436
+ * const result = fastifyDetector('/path/to/project', pkg)
437
+ * // => {
438
+ * // id: 'fastify',
439
+ * // name: 'Fastify',
440
+ * // version: '4.24.0',
441
+ * // confidence: 95,
442
+ * // detectedFrom: [
443
+ * // { type: 'package.json', field: 'dependencies.fastify' },
444
+ * // { type: 'package.json', field: 'dependencies (fastify plugins)' },
445
+ * // ],
446
+ * // }
447
+ * ```
1034
448
  */
1035
- function nestDetector(projectPath, packageJson) {
449
+ function fastifyDetector(projectPath, packageJson) {
1036
450
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1037
451
  const sources = [];
1038
452
  let confidence = 0;
1039
453
  let version;
1040
- let configPath;
1041
454
  const deps = collectAllDependencies(pkg);
1042
- if (deps['@nestjs/core']) {
1043
- confidence += 70;
1044
- version = parseVersionString(deps['@nestjs/core']);
1045
- sources.push({ type: 'package.json', field: 'dependencies.@nestjs/core' });
1046
- }
1047
- if (deps['@nestjs/common']) {
1048
- confidence += 15;
1049
- sources.push({ type: 'package.json', field: 'dependencies.@nestjs/common' });
455
+ if (deps['fastify']) {
456
+ confidence += 80;
457
+ version = parseVersionString(deps['fastify']);
458
+ sources.push({ type: 'package.json', field: 'dependencies.fastify' });
1050
459
  }
1051
- if (exists(join$1(projectPath, 'nest-cli.json'))) {
460
+ const fastifyPlugins = keys(deps).filter((d) => d.startsWith('@fastify/') || d.startsWith('fastify-'));
461
+ if (fastifyPlugins.length > 0) {
1052
462
  confidence += 15;
1053
- configPath = 'nest-cli.json';
1054
- sources.push({ type: 'config-file', path: 'nest-cli.json' });
463
+ sources.push({ type: 'package.json', field: 'dependencies (fastify plugins)' });
1055
464
  }
1056
- const nestPackages = keys(deps).filter((d) => d.startsWith('@nestjs/'));
1057
- if (nestPackages.length > 2) {
465
+ if (deps['@types/fastify']) {
1058
466
  confidence += 5;
1059
- sources.push({ type: 'package.json', field: 'dependencies (@nestjs packages)' });
467
+ sources.push({ type: 'package.json', field: 'dependencies.@types/fastify' });
1060
468
  }
1061
469
  if (confidence === 0) {
1062
470
  return null;
1063
471
  }
1064
472
  return {
1065
- id: 'nestjs',
1066
- name: 'NestJS',
473
+ id: 'fastify',
474
+ name: 'Fastify',
1067
475
  version,
1068
- configPath,
1069
476
  confidence: min(confidence, 100),
1070
477
  detectedFrom: sources,
1071
478
  };
1072
479
  }
1073
480
 
1074
481
  /**
1075
- * Detect Fastify in project.
482
+ * Detect Hono in project.
1076
483
  *
1077
484
  * @param projectPath - Project directory path
1078
485
  * @param packageJson - Optional pre-loaded package.json
1079
486
  * @returns Detection result or null if not detected
487
+ * @example Detecting Hono framework
488
+ * ```typescript
489
+ * const pkg = {
490
+ * dependencies: { hono: '^3.11.0', '@hono/node-server': '^1.3.0' },
491
+ * }
492
+ *
493
+ * const result = honoDetector('/path/to/project', pkg)
494
+ * // => {
495
+ * // id: 'hono',
496
+ * // name: 'Hono',
497
+ * // version: '3.11.0',
498
+ * // confidence: 100,
499
+ * // detectedFrom: [
500
+ * // { type: 'package.json', field: 'dependencies.hono' },
501
+ * // { type: 'package.json', field: 'dependencies (@hono adapters)' },
502
+ * // ],
503
+ * // }
504
+ * ```
1080
505
  */
1081
- function fastifyDetector(projectPath, packageJson) {
506
+ function honoDetector(projectPath, packageJson) {
1082
507
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1083
508
  const sources = [];
1084
509
  let confidence = 0;
1085
510
  let version;
1086
511
  const deps = collectAllDependencies(pkg);
1087
- if (deps['fastify']) {
1088
- confidence += 80;
1089
- version = parseVersionString(deps['fastify']);
1090
- sources.push({ type: 'package.json', field: 'dependencies.fastify' });
512
+ if (deps['hono']) {
513
+ confidence += 85;
514
+ version = parseVersionString(deps['hono']);
515
+ sources.push({ type: 'package.json', field: 'dependencies.hono' });
1091
516
  }
1092
- const fastifyPlugins = keys(deps).filter((d) => d.startsWith('@fastify/') || d.startsWith('fastify-'));
1093
- if (fastifyPlugins.length > 0) {
517
+ const honoAdapters = keys(deps).filter((d) => d.startsWith('@hono/'));
518
+ if (honoAdapters.length > 0) {
1094
519
  confidence += 15;
1095
- sources.push({ type: 'package.json', field: 'dependencies (fastify plugins)' });
1096
- }
1097
- if (deps['@types/fastify']) {
1098
- confidence += 5;
1099
- sources.push({ type: 'package.json', field: 'dependencies.@types/fastify' });
520
+ sources.push({ type: 'package.json', field: 'dependencies (@hono adapters)' });
1100
521
  }
1101
522
  if (confidence === 0) {
1102
523
  return null;
1103
524
  }
1104
525
  return {
1105
- id: 'fastify',
1106
- name: 'Fastify',
526
+ id: 'hono',
527
+ name: 'Hono',
1107
528
  version,
1108
529
  confidence: min(confidence, 100),
1109
530
  detectedFrom: sources,
@@ -1116,6 +537,26 @@ function fastifyDetector(projectPath, packageJson) {
1116
537
  * @param projectPath - Project directory path
1117
538
  * @param packageJson - Optional pre-loaded package.json
1118
539
  * @returns Detection result or null if not detected
540
+ * @example Detecting Koa framework
541
+ * ```typescript
542
+ * const pkg = {
543
+ * dependencies: { koa: '^2.14.2', 'koa-router': '^12.0.0' },
544
+ * devDependencies: { '@types/koa': '^2.13.9' },
545
+ * }
546
+ *
547
+ * const result = koaDetector('/path/to/project', pkg)
548
+ * // => {
549
+ * // id: 'koa',
550
+ * // name: 'Koa',
551
+ * // version: '2.14.2',
552
+ * // confidence: 100,
553
+ * // detectedFrom: [
554
+ * // { type: 'package.json', field: 'dependencies.koa' },
555
+ * // { type: 'package.json', field: 'dependencies.@types/koa' },
556
+ * // { type: 'package.json', field: 'dependencies (koa middleware)' },
557
+ * // ],
558
+ * // }
559
+ * ```
1119
560
  */
1120
561
  function koaDetector(projectPath, packageJson) {
1121
562
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -1150,35 +591,72 @@ function koaDetector(projectPath, packageJson) {
1150
591
  }
1151
592
 
1152
593
  /**
1153
- * Detect Hono in project.
594
+ * Detect NestJS in project.
1154
595
  *
1155
596
  * @param projectPath - Project directory path
1156
597
  * @param packageJson - Optional pre-loaded package.json
1157
598
  * @returns Detection result or null if not detected
599
+ * @example Detecting NestJS framework
600
+ * ```typescript
601
+ * // Project with nest-cli.json and NestJS packages
602
+ * const pkg = {
603
+ * dependencies: {
604
+ * '@nestjs/core': '^10.2.0',
605
+ * '@nestjs/common': '^10.2.0',
606
+ * '@nestjs/platform-express': '^10.2.0',
607
+ * },
608
+ * }
609
+ *
610
+ * const result = nestDetector('/path/to/nest-project', pkg)
611
+ * // => {
612
+ * // id: 'nestjs',
613
+ * // name: 'NestJS',
614
+ * // version: '10.2.0',
615
+ * // configPath: 'nest-cli.json', // if present
616
+ * // confidence: 100,
617
+ * // detectedFrom: [
618
+ * // { type: 'package.json', field: 'dependencies.@nestjs/core' },
619
+ * // { type: 'package.json', field: 'dependencies.@nestjs/common' },
620
+ * // { type: 'config-file', path: 'nest-cli.json' },
621
+ * // { type: 'package.json', field: 'dependencies (@nestjs packages)' },
622
+ * // ],
623
+ * // }
624
+ * ```
1158
625
  */
1159
- function honoDetector(projectPath, packageJson) {
626
+ function nestDetector(projectPath, packageJson) {
1160
627
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1161
628
  const sources = [];
1162
629
  let confidence = 0;
1163
630
  let version;
631
+ let configPath;
1164
632
  const deps = collectAllDependencies(pkg);
1165
- if (deps['hono']) {
1166
- confidence += 85;
1167
- version = parseVersionString(deps['hono']);
1168
- sources.push({ type: 'package.json', field: 'dependencies.hono' });
633
+ if (deps['@nestjs/core']) {
634
+ confidence += 70;
635
+ version = parseVersionString(deps['@nestjs/core']);
636
+ sources.push({ type: 'package.json', field: 'dependencies.@nestjs/core' });
1169
637
  }
1170
- const honoAdapters = keys(deps).filter((d) => d.startsWith('@hono/'));
1171
- if (honoAdapters.length > 0) {
638
+ if (deps['@nestjs/common']) {
1172
639
  confidence += 15;
1173
- sources.push({ type: 'package.json', field: 'dependencies (@hono adapters)' });
640
+ sources.push({ type: 'package.json', field: 'dependencies.@nestjs/common' });
641
+ }
642
+ if (exists(join$1(projectPath, 'nest-cli.json'))) {
643
+ confidence += 15;
644
+ configPath = 'nest-cli.json';
645
+ sources.push({ type: 'config-file', path: 'nest-cli.json' });
646
+ }
647
+ const nestPackages = keys(deps).filter((d) => d.startsWith('@nestjs/'));
648
+ if (nestPackages.length > 2) {
649
+ confidence += 5;
650
+ sources.push({ type: 'package.json', field: 'dependencies (@nestjs packages)' });
1174
651
  }
1175
652
  if (confidence === 0) {
1176
653
  return null;
1177
654
  }
1178
655
  return {
1179
- id: 'hono',
1180
- name: 'Hono',
656
+ id: 'nestjs',
657
+ name: 'NestJS',
1181
658
  version,
659
+ configPath,
1182
660
  confidence: min(confidence, 100),
1183
661
  detectedFrom: sources,
1184
662
  };
@@ -1193,52 +671,64 @@ const backendDetectors = [
1193
671
  { id: 'hono', name: 'Hono', detect: honoDetector },
1194
672
  ];
1195
673
 
1196
- /** Config patterns for Webpack */
1197
- const WEBPACK_CONFIG_PATTERNS = [
1198
- 'webpack.config.js',
1199
- 'webpack.config.ts',
1200
- 'webpack.config.cjs',
1201
- 'webpack.config.mjs',
1202
- 'webpack.config.babel.js',
1203
- ];
674
+ /** Config patterns for Babel */
675
+ const BABEL_CONFIG_PATTERNS = ['babel.config.js', 'babel.config.cjs', 'babel.config.mjs', 'babel.config.json', '.babelrc', '.babelrc.json', '.babelrc.js'];
1204
676
  /**
1205
- * Detect Webpack in project.
677
+ * Detect Babel in project.
1206
678
  *
1207
679
  * @param projectPath - Project directory path
1208
680
  * @param packageJson - Optional pre-loaded package.json
1209
681
  * @returns Detection result or null if not detected
682
+ *
683
+ * @example Detecting Babel compiler
684
+ * ```typescript
685
+ * const result = babelDetector('/path/to/project', {
686
+ * name: 'my-app',
687
+ * devDependencies: { '@babel/core': '^7.23.0', '@babel/preset-env': '^7.23.0' }
688
+ * })
689
+ * // => {
690
+ * // id: 'babel',
691
+ * // name: 'Babel',
692
+ * // version: '7.23.0',
693
+ * // confidence: 60,
694
+ * // detectedFrom: [
695
+ * // { type: 'package.json', field: 'dependencies.@babel/core' },
696
+ * // { type: 'package.json', field: 'dependencies (@babel packages)' }
697
+ * // ]
698
+ * // }
699
+ * ```
1210
700
  */
1211
- function webpackDetector(projectPath, packageJson) {
701
+ function babelDetector(projectPath, packageJson) {
1212
702
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1213
703
  const sources = [];
1214
704
  let confidence = 0;
1215
705
  let version;
1216
706
  const deps = collectAllDependencies(pkg);
1217
- if (deps['webpack']) {
707
+ if (deps['@babel/core']) {
1218
708
  confidence += 50;
1219
- version = parseVersionString(deps['webpack']);
1220
- sources.push({ type: 'package.json', field: 'dependencies.webpack' });
709
+ version = parseVersionString(deps['@babel/core']);
710
+ sources.push({ type: 'package.json', field: 'dependencies.@babel/core' });
1221
711
  }
1222
- const configPath = locateConfigFile(projectPath, WEBPACK_CONFIG_PATTERNS);
712
+ const configPath = locateConfigFile(projectPath, BABEL_CONFIG_PATTERNS);
1223
713
  if (configPath) {
1224
714
  confidence += 40;
1225
715
  sources.push({ type: 'config-file', path: configPath });
1226
716
  }
1227
- if (deps['webpack-cli']) {
1228
- confidence += 10;
1229
- sources.push({ type: 'package.json', field: 'dependencies.webpack-cli' });
717
+ if (pkg && 'babel' in pkg) {
718
+ confidence += 30;
719
+ sources.push({ type: 'package.json', field: 'babel' });
1230
720
  }
1231
- const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'webpack');
1232
- for (const name of scriptMatches) {
1233
- confidence = min(confidence + 5, 100);
1234
- sources.push({ type: 'package.json', field: `scripts.${name}` });
721
+ const babelPackages = keys(deps).filter((d) => d.startsWith('@babel/'));
722
+ if (babelPackages.length > 1) {
723
+ confidence += 10;
724
+ sources.push({ type: 'package.json', field: 'dependencies (@babel packages)' });
1235
725
  }
1236
726
  if (confidence === 0) {
1237
727
  return null;
1238
728
  }
1239
729
  return {
1240
- id: 'webpack',
1241
- name: 'Webpack',
730
+ id: 'babel',
731
+ name: 'Babel',
1242
732
  version,
1243
733
  configPath,
1244
734
  confidence: min(confidence, 100),
@@ -1246,94 +736,126 @@ function webpackDetector(projectPath, packageJson) {
1246
736
  };
1247
737
  }
1248
738
 
1249
- /** Config patterns for Vite */
1250
- const VITE_CONFIG_PATTERNS = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.cjs'];
1251
739
  /**
1252
- * Detect Vite in project.
740
+ * Detect esbuild in project.
1253
741
  *
1254
742
  * @param projectPath - Project directory path
1255
743
  * @param packageJson - Optional pre-loaded package.json
1256
744
  * @returns Detection result or null if not detected
745
+ *
746
+ * @example Detecting esbuild bundler
747
+ * ```typescript
748
+ * const result = esbuildDetector('/path/to/project', {
749
+ * name: 'my-lib',
750
+ * devDependencies: { 'esbuild': '^0.19.0' },
751
+ * scripts: { 'build': 'esbuild src/index.ts --bundle --outfile=dist/index.js' }
752
+ * })
753
+ * // => {
754
+ * // id: 'esbuild',
755
+ * // name: 'esbuild',
756
+ * // version: '0.19.0',
757
+ * // confidence: 80,
758
+ * // detectedFrom: [
759
+ * // { type: 'package.json', field: 'dependencies.esbuild' },
760
+ * // { type: 'package.json', field: 'scripts.build' }
761
+ * // ]
762
+ * // }
763
+ * ```
1257
764
  */
1258
- function viteDetector(projectPath, packageJson) {
765
+ function esbuildDetector(projectPath, packageJson) {
1259
766
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1260
767
  const sources = [];
1261
768
  let confidence = 0;
1262
769
  let version;
1263
770
  const deps = collectAllDependencies(pkg);
1264
- if (deps['vite']) {
1265
- confidence += 60;
1266
- version = parseVersionString(deps['vite']);
1267
- sources.push({ type: 'package.json', field: 'dependencies.vite' });
1268
- }
1269
- const configPath = locateConfigFile(projectPath, VITE_CONFIG_PATTERNS);
1270
- if (configPath) {
1271
- confidence += 35;
1272
- sources.push({ type: 'config-file', path: configPath });
771
+ if (deps['esbuild']) {
772
+ confidence += 70;
773
+ version = parseVersionString(deps['esbuild']);
774
+ sources.push({ type: 'package.json', field: 'dependencies.esbuild' });
1273
775
  }
1274
- if (deps['vitest']) {
1275
- confidence += 10;
1276
- sources.push({ type: 'package.json', field: 'dependencies.vitest' });
776
+ const esbuildPlugins = keys(deps).filter((d) => d.includes('esbuild-plugin') || d.includes('esbuild-'));
777
+ if (esbuildPlugins.length > 0) {
778
+ confidence += 15;
779
+ sources.push({ type: 'package.json', field: 'dependencies (esbuild plugins)' });
1277
780
  }
1278
- const vitePlugins = keys(deps).filter((d) => d.startsWith('vite-plugin-') || d.startsWith('@vitejs/'));
1279
- if (vitePlugins.length > 0) {
1280
- confidence += 10;
1281
- sources.push({ type: 'package.json', field: 'dependencies (vite plugins)' });
781
+ const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'esbuild');
782
+ for (const name of scriptMatches) {
783
+ confidence = min(confidence + 10, 100);
784
+ sources.push({ type: 'package.json', field: `scripts.${name}` });
1282
785
  }
1283
786
  if (confidence === 0) {
1284
787
  return null;
1285
788
  }
1286
789
  return {
1287
- id: 'vite',
1288
- name: 'Vite',
790
+ id: 'esbuild',
791
+ name: 'esbuild',
1289
792
  version,
1290
- configPath,
1291
793
  confidence: min(confidence, 100),
1292
794
  detectedFrom: sources,
1293
795
  };
1294
796
  }
1295
797
 
1296
- /** Config patterns for Rollup */
1297
- const ROLLUP_CONFIG_PATTERNS = ['rollup.config.js', 'rollup.config.ts', 'rollup.config.mjs', 'rollup.config.cjs'];
798
+ /** Config patterns for Parcel */
799
+ const PARCEL_CONFIG_PATTERNS = ['.parcelrc'];
1298
800
  /**
1299
- * Detect Rollup in project.
801
+ * Detect Parcel in project.
1300
802
  *
1301
803
  * @param projectPath - Project directory path
1302
804
  * @param packageJson - Optional pre-loaded package.json
1303
805
  * @returns Detection result or null if not detected
806
+ *
807
+ * @example Detecting Parcel bundler
808
+ * ```typescript
809
+ * const result = parcelDetector('/path/to/project', {
810
+ * name: 'my-app',
811
+ * devDependencies: { 'parcel': '^2.10.0' },
812
+ * scripts: { 'dev': 'parcel src/index.html', 'build': 'parcel build src/index.html' }
813
+ * })
814
+ * // => {
815
+ * // id: 'parcel',
816
+ * // name: 'Parcel',
817
+ * // version: '2.10.0',
818
+ * // confidence: 80,
819
+ * // detectedFrom: [
820
+ * // { type: 'package.json', field: 'dependencies.parcel' },
821
+ * // { type: 'package.json', field: 'scripts.dev' },
822
+ * // { type: 'package.json', field: 'scripts.build' }
823
+ * // ]
824
+ * // }
825
+ * ```
1304
826
  */
1305
- function rollupDetector(projectPath, packageJson) {
827
+ function parcelDetector(projectPath, packageJson) {
1306
828
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1307
829
  const sources = [];
1308
830
  let confidence = 0;
1309
831
  let version;
1310
832
  const deps = collectAllDependencies(pkg);
1311
- if (deps['rollup']) {
1312
- confidence += 55;
1313
- version = parseVersionString(deps['rollup']);
1314
- sources.push({ type: 'package.json', field: 'dependencies.rollup' });
1315
- }
1316
- const configPath = locateConfigFile(projectPath, ROLLUP_CONFIG_PATTERNS);
1317
- if (configPath) {
1318
- confidence += 40;
1319
- sources.push({ type: 'config-file', path: configPath });
833
+ if (deps['parcel']) {
834
+ confidence += 60;
835
+ version = parseVersionString(deps['parcel']);
836
+ sources.push({ type: 'package.json', field: 'dependencies.parcel' });
1320
837
  }
1321
- const rollupPlugins = keys(deps).filter((d) => d.startsWith('@rollup/') || d.startsWith('rollup-plugin-'));
1322
- if (rollupPlugins.length > 0) {
1323
- confidence += 10;
1324
- sources.push({ type: 'package.json', field: 'dependencies (rollup plugins)' });
838
+ if (deps['parcel-bundler']) {
839
+ confidence += 60;
840
+ version = parseVersionString(deps['parcel-bundler']);
841
+ sources.push({ type: 'package.json', field: 'dependencies.parcel-bundler' });
1325
842
  }
1326
- const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'rollup');
843
+ const configPath = locateConfigFile(projectPath, PARCEL_CONFIG_PATTERNS);
844
+ if (configPath) {
845
+ confidence += 30;
846
+ sources.push({ type: 'config-file', path: configPath });
847
+ }
848
+ const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'parcel');
1327
849
  for (const name of scriptMatches) {
1328
- confidence = min(confidence + 5, 100);
850
+ confidence = min(confidence + 10, 100);
1329
851
  sources.push({ type: 'package.json', field: `scripts.${name}` });
1330
852
  }
1331
853
  if (confidence === 0) {
1332
854
  return null;
1333
855
  }
1334
856
  return {
1335
- id: 'rollup',
1336
- name: 'Rollup',
857
+ id: 'parcel',
858
+ name: 'Parcel',
1337
859
  version,
1338
860
  configPath,
1339
861
  confidence: min(confidence, 100),
@@ -1341,86 +863,134 @@ function rollupDetector(projectPath, packageJson) {
1341
863
  };
1342
864
  }
1343
865
 
866
+ /** Config patterns for Rollup */
867
+ const ROLLUP_CONFIG_PATTERNS = ['rollup.config.js', 'rollup.config.ts', 'rollup.config.mjs', 'rollup.config.cjs'];
1344
868
  /**
1345
- * Detect esbuild in project.
869
+ * Detect Rollup in project.
1346
870
  *
1347
871
  * @param projectPath - Project directory path
1348
872
  * @param packageJson - Optional pre-loaded package.json
1349
873
  * @returns Detection result or null if not detected
874
+ *
875
+ * @example Detecting Rollup bundler
876
+ * ```typescript
877
+ * const result = rollupDetector('/path/to/project', {
878
+ * name: 'my-lib',
879
+ * devDependencies: {
880
+ * 'rollup': '^4.0.0',
881
+ * '@rollup/plugin-node-resolve': '^15.0.0',
882
+ * '@rollup/plugin-commonjs': '^25.0.0'
883
+ * }
884
+ * })
885
+ * // => {
886
+ * // id: 'rollup',
887
+ * // name: 'Rollup',
888
+ * // version: '4.0.0',
889
+ * // confidence: 65,
890
+ * // detectedFrom: [
891
+ * // { type: 'package.json', field: 'dependencies.rollup' },
892
+ * // { type: 'package.json', field: 'dependencies (rollup plugins)' }
893
+ * // ]
894
+ * // }
895
+ * ```
1350
896
  */
1351
- function esbuildDetector(projectPath, packageJson) {
897
+ function rollupDetector(projectPath, packageJson) {
1352
898
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1353
899
  const sources = [];
1354
900
  let confidence = 0;
1355
901
  let version;
1356
902
  const deps = collectAllDependencies(pkg);
1357
- if (deps['esbuild']) {
1358
- confidence += 70;
1359
- version = parseVersionString(deps['esbuild']);
1360
- sources.push({ type: 'package.json', field: 'dependencies.esbuild' });
903
+ if (deps['rollup']) {
904
+ confidence += 55;
905
+ version = parseVersionString(deps['rollup']);
906
+ sources.push({ type: 'package.json', field: 'dependencies.rollup' });
1361
907
  }
1362
- const esbuildPlugins = keys(deps).filter((d) => d.includes('esbuild-plugin') || d.includes('esbuild-'));
1363
- if (esbuildPlugins.length > 0) {
1364
- confidence += 15;
1365
- sources.push({ type: 'package.json', field: 'dependencies (esbuild plugins)' });
908
+ const configPath = locateConfigFile(projectPath, ROLLUP_CONFIG_PATTERNS);
909
+ if (configPath) {
910
+ confidence += 40;
911
+ sources.push({ type: 'config-file', path: configPath });
1366
912
  }
1367
- const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'esbuild');
913
+ const rollupPlugins = keys(deps).filter((d) => d.startsWith('@rollup/') || d.startsWith('rollup-plugin-'));
914
+ if (rollupPlugins.length > 0) {
915
+ confidence += 10;
916
+ sources.push({ type: 'package.json', field: 'dependencies (rollup plugins)' });
917
+ }
918
+ const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'rollup');
1368
919
  for (const name of scriptMatches) {
1369
- confidence = min(confidence + 10, 100);
920
+ confidence = min(confidence + 5, 100);
1370
921
  sources.push({ type: 'package.json', field: `scripts.${name}` });
1371
922
  }
1372
923
  if (confidence === 0) {
1373
924
  return null;
1374
925
  }
1375
926
  return {
1376
- id: 'esbuild',
1377
- name: 'esbuild',
927
+ id: 'rollup',
928
+ name: 'Rollup',
1378
929
  version,
930
+ configPath,
1379
931
  confidence: min(confidence, 100),
1380
932
  detectedFrom: sources,
1381
933
  };
1382
934
  }
1383
935
 
1384
- /** Config patterns for Babel */
1385
- const BABEL_CONFIG_PATTERNS = ['babel.config.js', 'babel.config.cjs', 'babel.config.mjs', 'babel.config.json', '.babelrc', '.babelrc.json', '.babelrc.js'];
936
+ /** Config patterns for SWC */
937
+ const SWC_CONFIG_PATTERNS = ['.swcrc', 'swc.config.js'];
1386
938
  /**
1387
- * Detect Babel in project.
939
+ * Detect SWC in project.
1388
940
  *
1389
941
  * @param projectPath - Project directory path
1390
942
  * @param packageJson - Optional pre-loaded package.json
1391
943
  * @returns Detection result or null if not detected
944
+ *
945
+ * @example Detecting SWC compiler
946
+ * ```typescript
947
+ * const result = swcDetector('/path/to/project', {
948
+ * name: 'my-app',
949
+ * devDependencies: { '@swc/core': '^1.3.0', '@swc/cli': '^0.1.0' }
950
+ * })
951
+ * // => {
952
+ * // id: 'swc',
953
+ * // name: 'SWC',
954
+ * // version: '1.3.0',
955
+ * // confidence: 70,
956
+ * // detectedFrom: [
957
+ * // { type: 'package.json', field: 'dependencies.@swc/core' },
958
+ * // { type: 'package.json', field: 'dependencies.@swc/cli' }
959
+ * // ]
960
+ * // }
961
+ * ```
1392
962
  */
1393
- function babelDetector(projectPath, packageJson) {
963
+ function swcDetector(projectPath, packageJson) {
1394
964
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1395
965
  const sources = [];
1396
966
  let confidence = 0;
1397
967
  let version;
1398
968
  const deps = collectAllDependencies(pkg);
1399
- if (deps['@babel/core']) {
1400
- confidence += 50;
1401
- version = parseVersionString(deps['@babel/core']);
1402
- sources.push({ type: 'package.json', field: 'dependencies.@babel/core' });
969
+ if (deps['@swc/core']) {
970
+ confidence += 60;
971
+ version = parseVersionString(deps['@swc/core']);
972
+ sources.push({ type: 'package.json', field: 'dependencies.@swc/core' });
1403
973
  }
1404
- const configPath = locateConfigFile(projectPath, BABEL_CONFIG_PATTERNS);
974
+ const configPath = locateConfigFile(projectPath, SWC_CONFIG_PATTERNS);
1405
975
  if (configPath) {
1406
- confidence += 40;
976
+ confidence += 35;
1407
977
  sources.push({ type: 'config-file', path: configPath });
1408
978
  }
1409
- if (pkg && 'babel' in pkg) {
1410
- confidence += 30;
1411
- sources.push({ type: 'package.json', field: 'babel' });
1412
- }
1413
- const babelPackages = keys(deps).filter((d) => d.startsWith('@babel/'));
1414
- if (babelPackages.length > 1) {
979
+ if (deps['@swc/cli']) {
1415
980
  confidence += 10;
1416
- sources.push({ type: 'package.json', field: 'dependencies (@babel packages)' });
981
+ sources.push({ type: 'package.json', field: 'dependencies.@swc/cli' });
982
+ }
983
+ const swcPlugins = keys(deps).filter((d) => d.startsWith('@swc/') || d.includes('swc-plugin'));
984
+ if (swcPlugins.length > 1) {
985
+ confidence += 5;
986
+ sources.push({ type: 'package.json', field: 'dependencies (@swc packages)' });
1417
987
  }
1418
988
  if (confidence === 0) {
1419
989
  return null;
1420
990
  }
1421
991
  return {
1422
- id: 'babel',
1423
- name: 'Babel',
992
+ id: 'swc',
993
+ name: 'SWC',
1424
994
  version,
1425
995
  configPath,
1426
996
  confidence: min(confidence, 100),
@@ -1428,46 +998,69 @@ function babelDetector(projectPath, packageJson) {
1428
998
  };
1429
999
  }
1430
1000
 
1431
- /** Config patterns for SWC */
1432
- const SWC_CONFIG_PATTERNS = ['.swcrc', 'swc.config.js'];
1001
+ /** Config patterns for Vite */
1002
+ const VITE_CONFIG_PATTERNS = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.cjs'];
1433
1003
  /**
1434
- * Detect SWC in project.
1004
+ * Detect Vite in project.
1435
1005
  *
1436
1006
  * @param projectPath - Project directory path
1437
1007
  * @param packageJson - Optional pre-loaded package.json
1438
1008
  * @returns Detection result or null if not detected
1009
+ *
1010
+ * @example Detecting Vite build tool
1011
+ * ```typescript
1012
+ * const result = viteDetector('/path/to/project', {
1013
+ * name: 'my-app',
1014
+ * devDependencies: {
1015
+ * 'vite': '^5.0.0',
1016
+ * '@vitejs/plugin-react': '^4.0.0',
1017
+ * 'vitest': '^1.0.0'
1018
+ * }
1019
+ * })
1020
+ * // => {
1021
+ * // id: 'vite',
1022
+ * // name: 'Vite',
1023
+ * // version: '5.0.0',
1024
+ * // confidence: 80,
1025
+ * // detectedFrom: [
1026
+ * // { type: 'package.json', field: 'dependencies.vite' },
1027
+ * // { type: 'package.json', field: 'dependencies.vitest' },
1028
+ * // { type: 'package.json', field: 'dependencies (vite plugins)' }
1029
+ * // ]
1030
+ * // }
1031
+ * ```
1439
1032
  */
1440
- function swcDetector(projectPath, packageJson) {
1033
+ function viteDetector(projectPath, packageJson) {
1441
1034
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1442
1035
  const sources = [];
1443
1036
  let confidence = 0;
1444
1037
  let version;
1445
1038
  const deps = collectAllDependencies(pkg);
1446
- if (deps['@swc/core']) {
1039
+ if (deps['vite']) {
1447
1040
  confidence += 60;
1448
- version = parseVersionString(deps['@swc/core']);
1449
- sources.push({ type: 'package.json', field: 'dependencies.@swc/core' });
1041
+ version = parseVersionString(deps['vite']);
1042
+ sources.push({ type: 'package.json', field: 'dependencies.vite' });
1450
1043
  }
1451
- const configPath = locateConfigFile(projectPath, SWC_CONFIG_PATTERNS);
1044
+ const configPath = locateConfigFile(projectPath, VITE_CONFIG_PATTERNS);
1452
1045
  if (configPath) {
1453
1046
  confidence += 35;
1454
1047
  sources.push({ type: 'config-file', path: configPath });
1455
1048
  }
1456
- if (deps['@swc/cli']) {
1049
+ if (deps['vitest']) {
1457
1050
  confidence += 10;
1458
- sources.push({ type: 'package.json', field: 'dependencies.@swc/cli' });
1051
+ sources.push({ type: 'package.json', field: 'dependencies.vitest' });
1459
1052
  }
1460
- const swcPlugins = keys(deps).filter((d) => d.startsWith('@swc/') || d.includes('swc-plugin'));
1461
- if (swcPlugins.length > 1) {
1462
- confidence += 5;
1463
- sources.push({ type: 'package.json', field: 'dependencies (@swc packages)' });
1053
+ const vitePlugins = keys(deps).filter((d) => d.startsWith('vite-plugin-') || d.startsWith('@vitejs/'));
1054
+ if (vitePlugins.length > 0) {
1055
+ confidence += 10;
1056
+ sources.push({ type: 'package.json', field: 'dependencies (vite plugins)' });
1464
1057
  }
1465
1058
  if (confidence === 0) {
1466
1059
  return null;
1467
1060
  }
1468
1061
  return {
1469
- id: 'swc',
1470
- name: 'SWC',
1062
+ id: 'vite',
1063
+ name: 'Vite',
1471
1064
  version,
1472
1065
  configPath,
1473
1066
  confidence: min(confidence, 100),
@@ -1475,47 +1068,72 @@ function swcDetector(projectPath, packageJson) {
1475
1068
  };
1476
1069
  }
1477
1070
 
1478
- /** Config patterns for Parcel */
1479
- const PARCEL_CONFIG_PATTERNS = ['.parcelrc'];
1071
+ /** Config patterns for Webpack */
1072
+ const WEBPACK_CONFIG_PATTERNS = [
1073
+ 'webpack.config.js',
1074
+ 'webpack.config.ts',
1075
+ 'webpack.config.cjs',
1076
+ 'webpack.config.mjs',
1077
+ 'webpack.config.babel.js',
1078
+ ];
1480
1079
  /**
1481
- * Detect Parcel in project.
1080
+ * Detect Webpack in project.
1482
1081
  *
1483
1082
  * @param projectPath - Project directory path
1484
1083
  * @param packageJson - Optional pre-loaded package.json
1485
1084
  * @returns Detection result or null if not detected
1085
+ *
1086
+ * @example Detecting Webpack bundler
1087
+ * ```typescript
1088
+ * const result = webpackDetector('/path/to/project', {
1089
+ * name: 'my-app',
1090
+ * devDependencies: { 'webpack': '^5.89.0', 'webpack-cli': '^5.1.0' },
1091
+ * scripts: { 'build': 'webpack --mode production' }
1092
+ * })
1093
+ * // => {
1094
+ * // id: 'webpack',
1095
+ * // name: 'Webpack',
1096
+ * // version: '5.89.0',
1097
+ * // confidence: 65,
1098
+ * // detectedFrom: [
1099
+ * // { type: 'package.json', field: 'dependencies.webpack' },
1100
+ * // { type: 'package.json', field: 'dependencies.webpack-cli' },
1101
+ * // { type: 'package.json', field: 'scripts.build' }
1102
+ * // ]
1103
+ * // }
1104
+ * ```
1486
1105
  */
1487
- function parcelDetector(projectPath, packageJson) {
1106
+ function webpackDetector(projectPath, packageJson) {
1488
1107
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1489
1108
  const sources = [];
1490
1109
  let confidence = 0;
1491
1110
  let version;
1492
1111
  const deps = collectAllDependencies(pkg);
1493
- if (deps['parcel']) {
1494
- confidence += 60;
1495
- version = parseVersionString(deps['parcel']);
1496
- sources.push({ type: 'package.json', field: 'dependencies.parcel' });
1497
- }
1498
- if (deps['parcel-bundler']) {
1499
- confidence += 60;
1500
- version = parseVersionString(deps['parcel-bundler']);
1501
- sources.push({ type: 'package.json', field: 'dependencies.parcel-bundler' });
1112
+ if (deps['webpack']) {
1113
+ confidence += 50;
1114
+ version = parseVersionString(deps['webpack']);
1115
+ sources.push({ type: 'package.json', field: 'dependencies.webpack' });
1502
1116
  }
1503
- const configPath = locateConfigFile(projectPath, PARCEL_CONFIG_PATTERNS);
1117
+ const configPath = locateConfigFile(projectPath, WEBPACK_CONFIG_PATTERNS);
1504
1118
  if (configPath) {
1505
- confidence += 30;
1119
+ confidence += 40;
1506
1120
  sources.push({ type: 'config-file', path: configPath });
1507
1121
  }
1508
- const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'parcel');
1122
+ if (deps['webpack-cli']) {
1123
+ confidence += 10;
1124
+ sources.push({ type: 'package.json', field: 'dependencies.webpack-cli' });
1125
+ }
1126
+ const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'webpack');
1509
1127
  for (const name of scriptMatches) {
1510
- confidence = min(confidence + 10, 100);
1128
+ confidence = min(confidence + 5, 100);
1511
1129
  sources.push({ type: 'package.json', field: `scripts.${name}` });
1512
1130
  }
1513
1131
  if (confidence === 0) {
1514
1132
  return null;
1515
1133
  }
1516
1134
  return {
1517
- id: 'parcel',
1518
- name: 'Parcel',
1135
+ id: 'webpack',
1136
+ name: 'Webpack',
1519
1137
  version,
1520
1138
  configPath,
1521
1139
  confidence: min(confidence, 100),
@@ -1535,81 +1153,187 @@ const buildToolDetectors = [
1535
1153
  ];
1536
1154
 
1537
1155
  /**
1538
- * Detect React in project.
1156
+ * Detect Angular in project.
1539
1157
  *
1540
1158
  * @param projectPath - Project directory path
1541
1159
  * @param packageJson - Optional pre-loaded package.json
1542
1160
  * @returns Detection result or null if not detected
1161
+ *
1162
+ * @example Detecting Angular framework
1163
+ * ```typescript
1164
+ * const result = angularDetector('/path/to/angular-app', {
1165
+ * dependencies: { '@angular/core': '^17.0.0', '@angular/cli': '^17.0.0' }
1166
+ * })
1167
+ * // => {
1168
+ * // id: 'angular',
1169
+ * // name: 'Angular',
1170
+ * // category: 'frontend',
1171
+ * // version: '17.0.0',
1172
+ * // confidence: 85,
1173
+ * // detectedFrom: [
1174
+ * // { type: 'package.json', field: 'dependencies.@angular/core' },
1175
+ * // { type: 'package.json', field: 'dependencies.@angular/cli' }
1176
+ * // ]
1177
+ * // }
1178
+ * ```
1543
1179
  */
1544
- function reactDetector(projectPath, packageJson) {
1180
+ function angularDetector(projectPath, packageJson) {
1545
1181
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1546
1182
  const sources = [];
1547
1183
  let confidence = 0;
1548
1184
  let version;
1549
- const metaFrameworks = [];
1550
1185
  const deps = collectAllDependencies(pkg);
1551
- if (deps['react']) {
1552
- confidence += 60;
1553
- version = parseVersionString(deps['react']);
1554
- sources.push({ type: 'package.json', field: 'dependencies.react' });
1186
+ if (deps['@angular/core']) {
1187
+ confidence += 70;
1188
+ version = parseVersionString(deps['@angular/core']);
1189
+ sources.push({ type: 'package.json', field: 'dependencies.@angular/core' });
1555
1190
  }
1556
- if (deps['react-dom']) {
1557
- confidence += 20;
1558
- sources.push({ type: 'package.json', field: 'dependencies.react-dom' });
1191
+ if (deps['@angular/cli']) {
1192
+ confidence += 15;
1193
+ sources.push({ type: 'package.json', field: 'dependencies.@angular/cli' });
1559
1194
  }
1560
- if (deps['react-native']) {
1561
- confidence += 20;
1562
- sources.push({ type: 'package.json', field: 'dependencies.react-native' });
1195
+ if (exists(join$1(projectPath, 'angular.json'))) {
1196
+ confidence += 15;
1197
+ sources.push({ type: 'config-file', path: 'angular.json' });
1563
1198
  }
1564
- const hasJsxFiles = exists(join$1(projectPath, 'src', 'App.tsx')) ||
1565
- exists(join$1(projectPath, 'src', 'App.jsx')) ||
1566
- exists(join$1(projectPath, 'src', 'index.tsx')) ||
1567
- exists(join$1(projectPath, 'src', 'index.jsx'));
1568
- if (hasJsxFiles) {
1569
- confidence += 10;
1570
- sources.push({ type: 'directory', path: 'src/*.tsx or src/*.jsx' });
1199
+ if (deps['angular'] && !deps['@angular/core']) {
1200
+ return {
1201
+ id: 'angularjs',
1202
+ name: 'AngularJS (Legacy)',
1203
+ category: 'frontend',
1204
+ version: parseVersionString(deps['angular']),
1205
+ confidence: 80,
1206
+ detectedFrom: [{ type: 'package.json', field: 'dependencies.angular' }],
1207
+ };
1571
1208
  }
1572
- if (deps['next']) {
1573
- metaFrameworks.push({
1574
- id: 'nextjs',
1575
- name: 'Next.js',
1576
- category: 'meta-framework',
1577
- version: parseVersionString(deps['next']),
1578
- confidence: 90,
1579
- detectedFrom: [{ type: 'package.json', field: 'dependencies.next' }],
1580
- });
1209
+ if (confidence === 0) {
1210
+ return null;
1581
1211
  }
1212
+ return {
1213
+ id: 'angular',
1214
+ name: 'Angular',
1215
+ category: 'frontend',
1216
+ version,
1217
+ confidence: min(confidence, 100),
1218
+ detectedFrom: sources,
1219
+ };
1220
+ }
1221
+
1222
+ /**
1223
+ * Detect Astro in project.
1224
+ *
1225
+ * @param projectPath - Project directory path
1226
+ * @param packageJson - Optional pre-loaded package.json
1227
+ * @returns Detection result or null if not detected
1228
+ *
1229
+ * @example Detecting Astro framework
1230
+ * ```typescript
1231
+ * const result = astroDetector('/path/to/astro-project', {
1232
+ * dependencies: { 'astro': '^4.0.0' }
1233
+ * })
1234
+ * // => {
1235
+ * // id: 'astro',
1236
+ * // name: 'Astro',
1237
+ * // category: 'meta-framework',
1238
+ * // version: '4.0.0',
1239
+ * // confidence: 70,
1240
+ * // detectedFrom: [{ type: 'package.json', field: 'dependencies.astro' }]
1241
+ * // }
1242
+ * ```
1243
+ */
1244
+ function astroDetector(projectPath, packageJson) {
1245
+ const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1246
+ const sources = [];
1247
+ let confidence = 0;
1248
+ let version;
1249
+ const deps = collectAllDependencies(pkg);
1250
+ if (deps['astro']) {
1251
+ confidence += 70;
1252
+ version = parseVersionString(deps['astro']);
1253
+ sources.push({ type: 'package.json', field: 'dependencies.astro' });
1254
+ }
1255
+ if (exists(join$1(projectPath, 'astro.config.mjs')) ||
1256
+ exists(join$1(projectPath, 'astro.config.ts')) ||
1257
+ exists(join$1(projectPath, 'astro.config.js'))) {
1258
+ confidence += 25;
1259
+ sources.push({ type: 'config-file', path: 'astro.config.*' });
1260
+ }
1261
+ if (exists(join$1(projectPath, 'src', 'pages'))) {
1262
+ confidence += 5;
1263
+ sources.push({ type: 'directory', path: 'src/pages/' });
1264
+ }
1265
+ if (confidence === 0) {
1266
+ return null;
1267
+ }
1268
+ return {
1269
+ id: 'astro',
1270
+ name: 'Astro',
1271
+ category: 'meta-framework',
1272
+ version,
1273
+ confidence: min(confidence, 100),
1274
+ detectedFrom: sources,
1275
+ };
1276
+ }
1277
+
1278
+ /**
1279
+ * Detect Gatsby in project.
1280
+ *
1281
+ * @param projectPath - Project directory path
1282
+ * @param packageJson - Optional pre-loaded package.json
1283
+ * @returns Detection result or null if not detected
1284
+ *
1285
+ * @example Detecting Gatsby framework
1286
+ * ```typescript
1287
+ * const result = gatsbyDetector('/path/to/gatsby-blog', {
1288
+ * dependencies: {
1289
+ * 'gatsby': '^5.0.0',
1290
+ * 'gatsby-plugin-image': '^3.0.0',
1291
+ * 'gatsby-source-filesystem': '^5.0.0'
1292
+ * }
1293
+ * })
1294
+ * // => {
1295
+ * // id: 'gatsby',
1296
+ * // name: 'Gatsby',
1297
+ * // category: 'meta-framework',
1298
+ * // version: '5.0.0',
1299
+ * // confidence: 75,
1300
+ * // detectedFrom: [
1301
+ * // { type: 'package.json', field: 'dependencies.gatsby' },
1302
+ * // { type: 'package.json', field: 'dependencies (gatsby plugins)' }
1303
+ * // ]
1304
+ * // }
1305
+ * ```
1306
+ */
1307
+ function gatsbyDetector(projectPath, packageJson) {
1308
+ const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1309
+ const sources = [];
1310
+ let confidence = 0;
1311
+ let version;
1312
+ const deps = collectAllDependencies(pkg);
1582
1313
  if (deps['gatsby']) {
1583
- metaFrameworks.push({
1584
- id: 'gatsby',
1585
- name: 'Gatsby',
1586
- category: 'meta-framework',
1587
- version: parseVersionString(deps['gatsby']),
1588
- confidence: 90,
1589
- detectedFrom: [{ type: 'package.json', field: 'dependencies.gatsby' }],
1590
- });
1314
+ confidence += 70;
1315
+ version = parseVersionString(deps['gatsby']);
1316
+ sources.push({ type: 'package.json', field: 'dependencies.gatsby' });
1591
1317
  }
1592
- if (deps['@remix-run/react'] || deps['remix']) {
1593
- metaFrameworks.push({
1594
- id: 'remix',
1595
- name: 'Remix',
1596
- category: 'meta-framework',
1597
- version: parseVersionString(deps['@remix-run/react'] ?? deps['remix']),
1598
- confidence: 90,
1599
- detectedFrom: [{ type: 'package.json', field: 'dependencies.@remix-run/react' }],
1600
- });
1318
+ if (exists(join$1(projectPath, 'gatsby-config.js')) || exists(join$1(projectPath, 'gatsby-config.ts'))) {
1319
+ confidence += 25;
1320
+ sources.push({ type: 'config-file', path: 'gatsby-config.*' });
1321
+ }
1322
+ const gatsbyPlugins = keys(deps).filter((d) => d.startsWith('gatsby-plugin-') || d.startsWith('gatsby-source-'));
1323
+ if (gatsbyPlugins.length > 0) {
1324
+ confidence += 5;
1325
+ sources.push({ type: 'package.json', field: 'dependencies (gatsby plugins)' });
1601
1326
  }
1602
1327
  if (confidence === 0) {
1603
1328
  return null;
1604
1329
  }
1605
1330
  return {
1606
- id: 'react',
1607
- name: 'React',
1608
- category: 'frontend',
1331
+ id: 'gatsby',
1332
+ name: 'Gatsby',
1333
+ category: 'meta-framework',
1609
1334
  version,
1610
1335
  confidence: min(confidence, 100),
1611
1336
  detectedFrom: sources,
1612
- metaFrameworks: metaFrameworks.length > 0 ? metaFrameworks : undefined,
1613
1337
  };
1614
1338
  }
1615
1339
 
@@ -1619,6 +1343,21 @@ function reactDetector(projectPath, packageJson) {
1619
1343
  * @param projectPath - Project directory path
1620
1344
  * @param packageJson - Optional pre-loaded package.json
1621
1345
  * @returns Detection result or null if not detected
1346
+ *
1347
+ * @example Detecting Next.js framework
1348
+ * ```typescript
1349
+ * const result = nextjsDetector('/path/to/nextjs-app', {
1350
+ * dependencies: { 'next': '^14.0.0', 'react': '^18.0.0' }
1351
+ * })
1352
+ * // => {
1353
+ * // id: 'nextjs',
1354
+ * // name: 'Next.js',
1355
+ * // category: 'meta-framework',
1356
+ * // version: '14.0.0',
1357
+ * // confidence: 70,
1358
+ * // detectedFrom: [{ type: 'package.json', field: 'dependencies.next' }]
1359
+ * // }
1360
+ * ```
1622
1361
  */
1623
1362
  function nextjsDetector(projectPath, packageJson) {
1624
1363
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -1658,37 +1397,52 @@ function nextjsDetector(projectPath, packageJson) {
1658
1397
  }
1659
1398
 
1660
1399
  /**
1661
- * Detect Remix in project.
1400
+ * Detect Nuxt in project.
1662
1401
  *
1663
1402
  * @param projectPath - Project directory path
1664
1403
  * @param packageJson - Optional pre-loaded package.json
1665
1404
  * @returns Detection result or null if not detected
1405
+ *
1406
+ * @example Detecting Nuxt framework
1407
+ * ```typescript
1408
+ * const result = nuxtDetector('/path/to/nuxt-app', {
1409
+ * dependencies: { 'nuxt': '^3.0.0', 'vue': '^3.0.0' }
1410
+ * })
1411
+ * // => {
1412
+ * // id: 'nuxt',
1413
+ * // name: 'Nuxt',
1414
+ * // category: 'meta-framework',
1415
+ * // version: '3.0.0',
1416
+ * // confidence: 70,
1417
+ * // detectedFrom: [{ type: 'package.json', field: 'dependencies.nuxt' }]
1418
+ * // }
1419
+ * ```
1666
1420
  */
1667
- function remixDetector(projectPath, packageJson) {
1421
+ function nuxtDetector(projectPath, packageJson) {
1668
1422
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1669
1423
  const sources = [];
1670
1424
  let confidence = 0;
1671
1425
  let version;
1672
1426
  const deps = collectAllDependencies(pkg);
1673
- if (deps['@remix-run/react']) {
1427
+ if (deps['nuxt'] || deps['nuxt3']) {
1674
1428
  confidence += 70;
1675
- version = parseVersionString(deps['@remix-run/react']);
1676
- sources.push({ type: 'package.json', field: 'dependencies.@remix-run/react' });
1429
+ version = parseVersionString(deps['nuxt'] ?? deps['nuxt3']);
1430
+ sources.push({ type: 'package.json', field: 'dependencies.nuxt' });
1677
1431
  }
1678
- if (deps['@remix-run/node'] || deps['@remix-run/cloudflare'] || deps['@remix-run/deno']) {
1679
- confidence += 20;
1680
- sources.push({ type: 'package.json', field: 'dependencies.@remix-run/*' });
1432
+ if (exists(join$1(projectPath, 'nuxt.config.js')) || exists(join$1(projectPath, 'nuxt.config.ts'))) {
1433
+ confidence += 25;
1434
+ sources.push({ type: 'config-file', path: 'nuxt.config.*' });
1681
1435
  }
1682
- if (exists(join$1(projectPath, 'remix.config.js')) || exists(join$1(projectPath, 'remix.config.ts'))) {
1683
- confidence += 10;
1684
- sources.push({ type: 'config-file', path: 'remix.config.*' });
1436
+ if (exists(join$1(projectPath, 'pages'))) {
1437
+ confidence += 5;
1438
+ sources.push({ type: 'directory', path: 'pages/' });
1685
1439
  }
1686
1440
  if (confidence === 0) {
1687
1441
  return null;
1688
1442
  }
1689
1443
  return {
1690
- id: 'remix',
1691
- name: 'Remix',
1444
+ id: 'nuxt',
1445
+ name: 'Nuxt',
1692
1446
  category: 'meta-framework',
1693
1447
  version,
1694
1448
  confidence: min(confidence, 100),
@@ -1697,39 +1451,59 @@ function remixDetector(projectPath, packageJson) {
1697
1451
  }
1698
1452
 
1699
1453
  /**
1700
- * Detect Gatsby in project.
1454
+ * Detect Qwik in project.
1701
1455
  *
1702
1456
  * @param projectPath - Project directory path
1703
1457
  * @param packageJson - Optional pre-loaded package.json
1704
1458
  * @returns Detection result or null if not detected
1459
+ *
1460
+ * @example Detecting Qwik framework
1461
+ * ```typescript
1462
+ * const result = qwikDetector('/path/to/qwik-app', {
1463
+ * dependencies: {
1464
+ * '@builder.io/qwik': '^1.0.0',
1465
+ * '@builder.io/qwik-city': '^1.0.0'
1466
+ * }
1467
+ * })
1468
+ * // => {
1469
+ * // id: 'qwik',
1470
+ * // name: 'Qwik',
1471
+ * // category: 'frontend',
1472
+ * // version: '1.0.0',
1473
+ * // confidence: 90,
1474
+ * // detectedFrom: [
1475
+ * // { type: 'package.json', field: 'dependencies.@builder.io/qwik' },
1476
+ * // { type: 'package.json', field: 'dependencies.@builder.io/qwik-city' }
1477
+ * // ]
1478
+ * // }
1479
+ * ```
1705
1480
  */
1706
- function gatsbyDetector(projectPath, packageJson) {
1481
+ function qwikDetector(projectPath, packageJson) {
1707
1482
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1708
1483
  const sources = [];
1709
1484
  let confidence = 0;
1710
1485
  let version;
1711
1486
  const deps = collectAllDependencies(pkg);
1712
- if (deps['gatsby']) {
1487
+ if (deps['@builder.io/qwik']) {
1713
1488
  confidence += 70;
1714
- version = parseVersionString(deps['gatsby']);
1715
- sources.push({ type: 'package.json', field: 'dependencies.gatsby' });
1489
+ version = parseVersionString(deps['@builder.io/qwik']);
1490
+ sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik' });
1716
1491
  }
1717
- if (exists(join$1(projectPath, 'gatsby-config.js')) || exists(join$1(projectPath, 'gatsby-config.ts'))) {
1718
- confidence += 25;
1719
- sources.push({ type: 'config-file', path: 'gatsby-config.*' });
1492
+ if (deps['@builder.io/qwik-city']) {
1493
+ confidence += 20;
1494
+ sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik-city' });
1720
1495
  }
1721
- const gatsbyPlugins = keys(deps).filter((d) => d.startsWith('gatsby-plugin-') || d.startsWith('gatsby-source-'));
1722
- if (gatsbyPlugins.length > 0) {
1723
- confidence += 5;
1724
- sources.push({ type: 'package.json', field: 'dependencies (gatsby plugins)' });
1496
+ if (exists(join$1(projectPath, 'qwik.config.ts')) || exists(join$1(projectPath, 'qwik.config.js'))) {
1497
+ confidence += 10;
1498
+ sources.push({ type: 'config-file', path: 'qwik.config.*' });
1725
1499
  }
1726
1500
  if (confidence === 0) {
1727
1501
  return null;
1728
1502
  }
1729
1503
  return {
1730
- id: 'gatsby',
1731
- name: 'Gatsby',
1732
- category: 'meta-framework',
1504
+ id: 'qwik',
1505
+ name: 'Qwik',
1506
+ category: 'frontend',
1733
1507
  version,
1734
1508
  confidence: min(confidence, 100),
1735
1509
  detectedFrom: sources,
@@ -1737,53 +1511,94 @@ function gatsbyDetector(projectPath, packageJson) {
1737
1511
  }
1738
1512
 
1739
1513
  /**
1740
- * Detect Vue in project.
1514
+ * Detect React in project.
1741
1515
  *
1742
1516
  * @param projectPath - Project directory path
1743
1517
  * @param packageJson - Optional pre-loaded package.json
1744
1518
  * @returns Detection result or null if not detected
1519
+ *
1520
+ * @example Detecting React library
1521
+ * ```typescript
1522
+ * const result = reactDetector('/path/to/react-app', {
1523
+ * dependencies: { 'react': '^18.0.0', 'react-dom': '^18.0.0' }
1524
+ * })
1525
+ * // => {
1526
+ * // id: 'react',
1527
+ * // name: 'React',
1528
+ * // category: 'frontend',
1529
+ * // version: '18.0.0',
1530
+ * // confidence: 80,
1531
+ * // detectedFrom: [
1532
+ * // { type: 'package.json', field: 'dependencies.react' },
1533
+ * // { type: 'package.json', field: 'dependencies.react-dom' }
1534
+ * // ]
1535
+ * // }
1536
+ * ```
1745
1537
  */
1746
- function vueDetector(projectPath, packageJson) {
1538
+ function reactDetector(projectPath, packageJson) {
1747
1539
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1748
1540
  const sources = [];
1749
1541
  let confidence = 0;
1750
1542
  let version;
1751
1543
  const metaFrameworks = [];
1752
1544
  const deps = collectAllDependencies(pkg);
1753
- if (deps['vue']) {
1754
- confidence += 70;
1755
- version = parseVersionString(deps['vue']);
1756
- sources.push({ type: 'package.json', field: 'dependencies.vue' });
1545
+ if (deps['react']) {
1546
+ confidence += 60;
1547
+ version = parseVersionString(deps['react']);
1548
+ sources.push({ type: 'package.json', field: 'dependencies.react' });
1757
1549
  }
1758
- if (deps['@vue/cli-service']) {
1759
- confidence += 15;
1760
- sources.push({ type: 'package.json', field: 'dependencies.@vue/cli-service' });
1550
+ if (deps['react-dom']) {
1551
+ confidence += 20;
1552
+ sources.push({ type: 'package.json', field: 'dependencies.react-dom' });
1761
1553
  }
1762
- const hasVueFiles = exists(join$1(projectPath, 'src', 'App.vue')) || exists(join$1(projectPath, 'src', 'main.vue'));
1763
- if (hasVueFiles) {
1554
+ if (deps['react-native']) {
1555
+ confidence += 20;
1556
+ sources.push({ type: 'package.json', field: 'dependencies.react-native' });
1557
+ }
1558
+ const hasJsxFiles = exists(join$1(projectPath, 'src', 'App.tsx')) ||
1559
+ exists(join$1(projectPath, 'src', 'App.jsx')) ||
1560
+ exists(join$1(projectPath, 'src', 'index.tsx')) ||
1561
+ exists(join$1(projectPath, 'src', 'index.jsx'));
1562
+ if (hasJsxFiles) {
1764
1563
  confidence += 10;
1765
- sources.push({ type: 'directory', path: 'src/*.vue' });
1564
+ sources.push({ type: 'directory', path: 'src/*.tsx or src/*.jsx' });
1766
1565
  }
1767
- if (exists(join$1(projectPath, 'vue.config.js'))) {
1768
- confidence += 5;
1769
- sources.push({ type: 'config-file', path: 'vue.config.js' });
1566
+ if (deps['next']) {
1567
+ metaFrameworks.push({
1568
+ id: 'nextjs',
1569
+ name: 'Next.js',
1570
+ category: 'meta-framework',
1571
+ version: parseVersionString(deps['next']),
1572
+ confidence: 90,
1573
+ detectedFrom: [{ type: 'package.json', field: 'dependencies.next' }],
1574
+ });
1770
1575
  }
1771
- if (deps['nuxt'] || deps['nuxt3']) {
1576
+ if (deps['gatsby']) {
1772
1577
  metaFrameworks.push({
1773
- id: 'nuxt',
1774
- name: 'Nuxt',
1578
+ id: 'gatsby',
1579
+ name: 'Gatsby',
1775
1580
  category: 'meta-framework',
1776
- version: parseVersionString(deps['nuxt'] ?? deps['nuxt3']),
1581
+ version: parseVersionString(deps['gatsby']),
1777
1582
  confidence: 90,
1778
- detectedFrom: [{ type: 'package.json', field: 'dependencies.nuxt' }],
1583
+ detectedFrom: [{ type: 'package.json', field: 'dependencies.gatsby' }],
1584
+ });
1585
+ }
1586
+ if (deps['@remix-run/react'] || deps['remix']) {
1587
+ metaFrameworks.push({
1588
+ id: 'remix',
1589
+ name: 'Remix',
1590
+ category: 'meta-framework',
1591
+ version: parseVersionString(deps['@remix-run/react'] ?? deps['remix']),
1592
+ confidence: 90,
1593
+ detectedFrom: [{ type: 'package.json', field: 'dependencies.@remix-run/react' }],
1779
1594
  });
1780
1595
  }
1781
1596
  if (confidence === 0) {
1782
1597
  return null;
1783
1598
  }
1784
1599
  return {
1785
- id: 'vue',
1786
- name: 'Vue',
1600
+ id: 'react',
1601
+ name: 'React',
1787
1602
  category: 'frontend',
1788
1603
  version,
1789
1604
  confidence: min(confidence, 100),
@@ -1793,37 +1608,58 @@ function vueDetector(projectPath, packageJson) {
1793
1608
  }
1794
1609
 
1795
1610
  /**
1796
- * Detect Nuxt in project.
1611
+ * Detect Remix in project.
1797
1612
  *
1798
1613
  * @param projectPath - Project directory path
1799
1614
  * @param packageJson - Optional pre-loaded package.json
1800
1615
  * @returns Detection result or null if not detected
1616
+ *
1617
+ * @example Detecting Remix framework
1618
+ * ```typescript
1619
+ * const result = remixDetector('/path/to/remix-app', {
1620
+ * dependencies: {
1621
+ * '@remix-run/react': '^2.0.0',
1622
+ * '@remix-run/node': '^2.0.0'
1623
+ * }
1624
+ * })
1625
+ * // => {
1626
+ * // id: 'remix',
1627
+ * // name: 'Remix',
1628
+ * // category: 'meta-framework',
1629
+ * // version: '2.0.0',
1630
+ * // confidence: 90,
1631
+ * // detectedFrom: [
1632
+ * // { type: 'package.json', field: 'dependencies.@remix-run/react' },
1633
+ * // { type: 'package.json', field: 'dependencies.@remix-run/*' }
1634
+ * // ]
1635
+ * // }
1636
+ * ```
1801
1637
  */
1802
- function nuxtDetector(projectPath, packageJson) {
1638
+ function remixDetector(projectPath, packageJson) {
1803
1639
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1804
1640
  const sources = [];
1805
1641
  let confidence = 0;
1806
1642
  let version;
1807
1643
  const deps = collectAllDependencies(pkg);
1808
- if (deps['nuxt'] || deps['nuxt3']) {
1644
+ if (deps['@remix-run/react']) {
1809
1645
  confidence += 70;
1810
- version = parseVersionString(deps['nuxt'] ?? deps['nuxt3']);
1811
- sources.push({ type: 'package.json', field: 'dependencies.nuxt' });
1646
+ version = parseVersionString(deps['@remix-run/react']);
1647
+ sources.push({ type: 'package.json', field: 'dependencies.@remix-run/react' });
1812
1648
  }
1813
- if (exists(join$1(projectPath, 'nuxt.config.js')) || exists(join$1(projectPath, 'nuxt.config.ts'))) {
1814
- confidence += 25;
1815
- sources.push({ type: 'config-file', path: 'nuxt.config.*' });
1649
+ if (deps['@remix-run/node'] || deps['@remix-run/cloudflare'] || deps['@remix-run/deno']) {
1650
+ confidence += 20;
1651
+ sources.push({ type: 'package.json', field: 'dependencies.@remix-run/*' });
1816
1652
  }
1817
- if (exists(join$1(projectPath, 'pages'))) {
1818
- confidence += 5;
1819
- sources.push({ type: 'directory', path: 'pages/' });
1653
+ if (exists(join$1(projectPath, 'remix.config.js')) || exists(join$1(projectPath, 'remix.config.ts'))) {
1654
+ confidence += 10;
1655
+ sources.push({ type: 'config-file', path: 'remix.config.*' });
1820
1656
  }
1821
1657
  if (confidence === 0) {
1822
1658
  return null;
1823
1659
  }
1824
1660
  return {
1825
- id: 'nuxt',
1826
- name: 'Nuxt',
1661
+ id: 'remix',
1662
+ name: 'Remix',
1827
1663
  category: 'meta-framework',
1828
1664
  version,
1829
1665
  confidence: min(confidence, 100),
@@ -1832,47 +1668,55 @@ function nuxtDetector(projectPath, packageJson) {
1832
1668
  }
1833
1669
 
1834
1670
  /**
1835
- * Detect Angular in project.
1671
+ * Detect Solid in project.
1836
1672
  *
1837
1673
  * @param projectPath - Project directory path
1838
1674
  * @param packageJson - Optional pre-loaded package.json
1839
1675
  * @returns Detection result or null if not detected
1676
+ *
1677
+ * @example Detecting Solid.js framework
1678
+ * ```typescript
1679
+ * const result = solidDetector('/path/to/solid-app', {
1680
+ * dependencies: { 'solid-js': '^1.8.0', 'vite-plugin-solid': '^2.0.0' }
1681
+ * })
1682
+ * // => {
1683
+ * // id: 'solid',
1684
+ * // name: 'Solid',
1685
+ * // category: 'frontend',
1686
+ * // version: '1.8.0',
1687
+ * // confidence: 90,
1688
+ * // detectedFrom: [
1689
+ * // { type: 'package.json', field: 'dependencies.solid-js' },
1690
+ * // { type: 'package.json', field: 'dependencies.vite-plugin-solid' }
1691
+ * // ]
1692
+ * // }
1693
+ * ```
1840
1694
  */
1841
- function angularDetector(projectPath, packageJson) {
1695
+ function solidDetector(projectPath, packageJson) {
1842
1696
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1843
1697
  const sources = [];
1844
1698
  let confidence = 0;
1845
1699
  let version;
1846
1700
  const deps = collectAllDependencies(pkg);
1847
- if (deps['@angular/core']) {
1701
+ if (deps['solid-js']) {
1848
1702
  confidence += 70;
1849
- version = parseVersionString(deps['@angular/core']);
1850
- sources.push({ type: 'package.json', field: 'dependencies.@angular/core' });
1851
- }
1852
- if (deps['@angular/cli']) {
1853
- confidence += 15;
1854
- sources.push({ type: 'package.json', field: 'dependencies.@angular/cli' });
1703
+ version = parseVersionString(deps['solid-js']);
1704
+ sources.push({ type: 'package.json', field: 'dependencies.solid-js' });
1855
1705
  }
1856
- if (exists(join$1(projectPath, 'angular.json'))) {
1857
- confidence += 15;
1858
- sources.push({ type: 'config-file', path: 'angular.json' });
1706
+ if (deps['vite-plugin-solid']) {
1707
+ confidence += 20;
1708
+ sources.push({ type: 'package.json', field: 'dependencies.vite-plugin-solid' });
1859
1709
  }
1860
- if (deps['angular'] && !deps['@angular/core']) {
1861
- return {
1862
- id: 'angularjs',
1863
- name: 'AngularJS (Legacy)',
1864
- category: 'frontend',
1865
- version: parseVersionString(deps['angular']),
1866
- confidence: 80,
1867
- detectedFrom: [{ type: 'package.json', field: 'dependencies.angular' }],
1868
- };
1710
+ if (deps['solid-start'] || deps['@solidjs/start']) {
1711
+ confidence += 10;
1712
+ sources.push({ type: 'package.json', field: 'dependencies.solid-start' });
1869
1713
  }
1870
1714
  if (confidence === 0) {
1871
1715
  return null;
1872
1716
  }
1873
1717
  return {
1874
- id: 'angular',
1875
- name: 'Angular',
1718
+ id: 'solid',
1719
+ name: 'Solid',
1876
1720
  category: 'frontend',
1877
1721
  version,
1878
1722
  confidence: min(confidence, 100),
@@ -1886,6 +1730,21 @@ function angularDetector(projectPath, packageJson) {
1886
1730
  * @param projectPath - Project directory path
1887
1731
  * @param packageJson - Optional pre-loaded package.json
1888
1732
  * @returns Detection result or null if not detected
1733
+ *
1734
+ * @example Detecting Svelte framework
1735
+ * ```typescript
1736
+ * const result = svelteDetector('/path/to/svelte-app', {
1737
+ * devDependencies: { 'svelte': '^4.0.0' }
1738
+ * })
1739
+ * // => {
1740
+ * // id: 'svelte',
1741
+ * // name: 'Svelte',
1742
+ * // category: 'frontend',
1743
+ * // version: '4.0.0',
1744
+ * // confidence: 70,
1745
+ * // detectedFrom: [{ type: 'package.json', field: 'dependencies.svelte' }]
1746
+ * // }
1747
+ * ```
1889
1748
  */
1890
1749
  function svelteDetector(projectPath, packageJson) {
1891
1750
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -1938,6 +1797,21 @@ function svelteDetector(projectPath, packageJson) {
1938
1797
  * @param projectPath - Project directory path
1939
1798
  * @param packageJson - Optional pre-loaded package.json
1940
1799
  * @returns Detection result or null if not detected
1800
+ *
1801
+ * @example Detecting SvelteKit framework
1802
+ * ```typescript
1803
+ * const result = sveltekitDetector('/path/to/sveltekit-app', {
1804
+ * devDependencies: { '@sveltejs/kit': '^2.0.0', 'svelte': '^4.0.0' }
1805
+ * })
1806
+ * // => {
1807
+ * // id: 'sveltekit',
1808
+ * // name: 'SvelteKit',
1809
+ * // category: 'meta-framework',
1810
+ * // version: '2.0.0',
1811
+ * // confidence: 70,
1812
+ * // detectedFrom: [{ type: 'package.json', field: 'dependencies.@sveltejs/kit' }]
1813
+ * // }
1814
+ * ```
1941
1815
  */
1942
1816
  function sveltekitDetector(projectPath, packageJson) {
1943
1817
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -1972,121 +1846,76 @@ function sveltekitDetector(projectPath, packageJson) {
1972
1846
  }
1973
1847
 
1974
1848
  /**
1975
- * Detect Solid in project.
1849
+ * Detect Vue in project.
1976
1850
  *
1977
1851
  * @param projectPath - Project directory path
1978
1852
  * @param packageJson - Optional pre-loaded package.json
1979
1853
  * @returns Detection result or null if not detected
1980
- */
1981
- function solidDetector(projectPath, packageJson) {
1982
- const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1983
- const sources = [];
1984
- let confidence = 0;
1985
- let version;
1986
- const deps = collectAllDependencies(pkg);
1987
- if (deps['solid-js']) {
1988
- confidence += 70;
1989
- version = parseVersionString(deps['solid-js']);
1990
- sources.push({ type: 'package.json', field: 'dependencies.solid-js' });
1991
- }
1992
- if (deps['vite-plugin-solid']) {
1993
- confidence += 20;
1994
- sources.push({ type: 'package.json', field: 'dependencies.vite-plugin-solid' });
1995
- }
1996
- if (deps['solid-start'] || deps['@solidjs/start']) {
1997
- confidence += 10;
1998
- sources.push({ type: 'package.json', field: 'dependencies.solid-start' });
1999
- }
2000
- if (confidence === 0) {
2001
- return null;
2002
- }
2003
- return {
2004
- id: 'solid',
2005
- name: 'Solid',
2006
- category: 'frontend',
2007
- version,
2008
- confidence: min(confidence, 100),
2009
- detectedFrom: sources,
2010
- };
2011
- }
2012
-
2013
- /**
2014
- * Detect Qwik in project.
2015
1854
  *
2016
- * @param projectPath - Project directory path
2017
- * @param packageJson - Optional pre-loaded package.json
2018
- * @returns Detection result or null if not detected
1855
+ * @example Detecting Vue.js framework
1856
+ * ```typescript
1857
+ * const result = vueDetector('/path/to/vue-app', {
1858
+ * dependencies: { 'vue': '^3.0.0', '@vue/cli-service': '^5.0.0' }
1859
+ * })
1860
+ * // => {
1861
+ * // id: 'vue',
1862
+ * // name: 'Vue',
1863
+ * // category: 'frontend',
1864
+ * // version: '3.0.0',
1865
+ * // confidence: 85,
1866
+ * // detectedFrom: [
1867
+ * // { type: 'package.json', field: 'dependencies.vue' },
1868
+ * // { type: 'package.json', field: 'dependencies.@vue/cli-service' }
1869
+ * // ]
1870
+ * // }
1871
+ * ```
2019
1872
  */
2020
- function qwikDetector(projectPath, packageJson) {
1873
+ function vueDetector(projectPath, packageJson) {
2021
1874
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2022
1875
  const sources = [];
2023
1876
  let confidence = 0;
2024
1877
  let version;
1878
+ const metaFrameworks = [];
2025
1879
  const deps = collectAllDependencies(pkg);
2026
- if (deps['@builder.io/qwik']) {
1880
+ if (deps['vue']) {
2027
1881
  confidence += 70;
2028
- version = parseVersionString(deps['@builder.io/qwik']);
2029
- sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik' });
1882
+ version = parseVersionString(deps['vue']);
1883
+ sources.push({ type: 'package.json', field: 'dependencies.vue' });
2030
1884
  }
2031
- if (deps['@builder.io/qwik-city']) {
2032
- confidence += 20;
2033
- sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik-city' });
1885
+ if (deps['@vue/cli-service']) {
1886
+ confidence += 15;
1887
+ sources.push({ type: 'package.json', field: 'dependencies.@vue/cli-service' });
2034
1888
  }
2035
- if (exists(join$1(projectPath, 'qwik.config.ts')) || exists(join$1(projectPath, 'qwik.config.js'))) {
1889
+ const hasVueFiles = exists(join$1(projectPath, 'src', 'App.vue')) || exists(join$1(projectPath, 'src', 'main.vue'));
1890
+ if (hasVueFiles) {
2036
1891
  confidence += 10;
2037
- sources.push({ type: 'config-file', path: 'qwik.config.*' });
2038
- }
2039
- if (confidence === 0) {
2040
- return null;
2041
- }
2042
- return {
2043
- id: 'qwik',
2044
- name: 'Qwik',
2045
- category: 'frontend',
2046
- version,
2047
- confidence: min(confidence, 100),
2048
- detectedFrom: sources,
2049
- };
2050
- }
2051
-
2052
- /**
2053
- * Detect Astro in project.
2054
- *
2055
- * @param projectPath - Project directory path
2056
- * @param packageJson - Optional pre-loaded package.json
2057
- * @returns Detection result or null if not detected
2058
- */
2059
- function astroDetector(projectPath, packageJson) {
2060
- const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2061
- const sources = [];
2062
- let confidence = 0;
2063
- let version;
2064
- const deps = collectAllDependencies(pkg);
2065
- if (deps['astro']) {
2066
- confidence += 70;
2067
- version = parseVersionString(deps['astro']);
2068
- sources.push({ type: 'package.json', field: 'dependencies.astro' });
2069
- }
2070
- if (exists(join$1(projectPath, 'astro.config.mjs')) ||
2071
- exists(join$1(projectPath, 'astro.config.ts')) ||
2072
- exists(join$1(projectPath, 'astro.config.js'))) {
2073
- confidence += 25;
2074
- sources.push({ type: 'config-file', path: 'astro.config.*' });
1892
+ sources.push({ type: 'directory', path: 'src/*.vue' });
2075
1893
  }
2076
- if (exists(join$1(projectPath, 'src', 'pages'))) {
1894
+ if (exists(join$1(projectPath, 'vue.config.js'))) {
2077
1895
  confidence += 5;
2078
- sources.push({ type: 'directory', path: 'src/pages/' });
1896
+ sources.push({ type: 'config-file', path: 'vue.config.js' });
1897
+ }
1898
+ if (deps['nuxt'] || deps['nuxt3']) {
1899
+ metaFrameworks.push({
1900
+ id: 'nuxt',
1901
+ name: 'Nuxt',
1902
+ category: 'meta-framework',
1903
+ version: parseVersionString(deps['nuxt'] ?? deps['nuxt3']),
1904
+ confidence: 90,
1905
+ detectedFrom: [{ type: 'package.json', field: 'dependencies.nuxt' }],
1906
+ });
2079
1907
  }
2080
1908
  if (confidence === 0) {
2081
1909
  return null;
2082
1910
  }
2083
1911
  return {
2084
- id: 'astro',
2085
- name: 'Astro',
2086
- category: 'meta-framework',
1912
+ id: 'vue',
1913
+ name: 'Vue',
1914
+ category: 'frontend',
2087
1915
  version,
2088
1916
  confidence: min(confidence, 100),
2089
1917
  detectedFrom: sources,
1918
+ metaFrameworks: metaFrameworks.length > 0 ? metaFrameworks : undefined,
2090
1919
  };
2091
1920
  }
2092
1921
 
@@ -2113,6 +1942,14 @@ const frameworkDetectors = [
2113
1942
  * @param projectPath - Project directory path
2114
1943
  * @param packageJson - Optional pre-loaded package.json
2115
1944
  * @returns Detection result or null if not detected
1945
+ *
1946
+ * @example Detecting AngularJS framework
1947
+ * ```typescript
1948
+ * const result = angularJSDetector('/path/to/project', {
1949
+ * dependencies: { angular: '^1.8.0', 'angular-route': '^1.8.0' },
1950
+ * })
1951
+ * // => { id: 'angularjs', name: 'AngularJS', confidence: 85, version: '1.8.0', ... }
1952
+ * ```
2116
1953
  */
2117
1954
  function angularJSDetector(projectPath, packageJson) {
2118
1955
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2156,6 +1993,14 @@ function angularJSDetector(projectPath, packageJson) {
2156
1993
  * @param projectPath - Project directory path
2157
1994
  * @param packageJson - Optional pre-loaded package.json
2158
1995
  * @returns Detection result or null if not detected
1996
+ *
1997
+ * @example Detecting Backbone.js framework
1998
+ * ```typescript
1999
+ * const result = backboneDetector('/path/to/project', {
2000
+ * dependencies: { backbone: '^1.4.0', underscore: '^1.13.0' },
2001
+ * })
2002
+ * // => { id: 'backbone', name: 'Backbone.js', confidence: 85, version: '1.4.0', ... }
2003
+ * ```
2159
2004
  */
2160
2005
  function backboneDetector(projectPath, packageJson) {
2161
2006
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2199,6 +2044,15 @@ function backboneDetector(projectPath, packageJson) {
2199
2044
  * @param projectPath - Project directory path
2200
2045
  * @param packageJson - Optional pre-loaded package.json
2201
2046
  * @returns Detection result or null if not detected
2047
+ *
2048
+ * @example Detecting Ember.js framework
2049
+ * ```typescript
2050
+ * const result = emberDetector('/path/to/project', {
2051
+ * dependencies: { 'ember-source': '^4.0.0' },
2052
+ * devDependencies: { 'ember-cli': '^4.0.0' },
2053
+ * })
2054
+ * // => { id: 'ember', name: 'Ember.js', confidence: 90, version: '4.0.0', ... }
2055
+ * ```
2202
2056
  */
2203
2057
  function emberDetector(projectPath, packageJson) {
2204
2058
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2238,6 +2092,14 @@ function emberDetector(projectPath, packageJson) {
2238
2092
  * @param projectPath - Project directory path
2239
2093
  * @param packageJson - Optional pre-loaded package.json
2240
2094
  * @returns Detection result or null if not detected
2095
+ *
2096
+ * @example Detecting jQuery library
2097
+ * ```typescript
2098
+ * const result = jqueryDetector('/path/to/project', {
2099
+ * dependencies: { jquery: '^3.6.0', 'jquery-ui': '^1.13.0' },
2100
+ * })
2101
+ * // => { id: 'jquery', name: 'jQuery', confidence: 90, version: '3.6.0', ... }
2102
+ * ```
2241
2103
  */
2242
2104
  function jqueryDetector(projectPath, packageJson) {
2243
2105
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2250,35 +2112,85 @@ function jqueryDetector(projectPath, packageJson) {
2250
2112
  version = parseVersionString(deps['jquery']);
2251
2113
  sources.push({ type: 'package.json', field: 'dependencies.jquery' });
2252
2114
  }
2253
- if (deps['jquery-ui']) {
2254
- confidence += 10;
2255
- sources.push({ type: 'package.json', field: 'dependencies.jquery-ui' });
2115
+ if (deps['jquery-ui']) {
2116
+ confidence += 10;
2117
+ sources.push({ type: 'package.json', field: 'dependencies.jquery-ui' });
2118
+ }
2119
+ if (deps['jquery-validation']) {
2120
+ confidence += 5;
2121
+ sources.push({ type: 'package.json', field: 'dependencies.jquery-validation' });
2122
+ }
2123
+ if (confidence === 0) {
2124
+ return null;
2125
+ }
2126
+ return {
2127
+ id: 'jquery',
2128
+ name: 'jQuery',
2129
+ category: 'legacy-frontend',
2130
+ version,
2131
+ confidence: min(confidence, 100),
2132
+ detectedFrom: sources,
2133
+ };
2134
+ }
2135
+
2136
+ /** All legacy framework detectors */
2137
+ const legacyDetectors = [
2138
+ { id: 'angularjs', name: 'AngularJS', category: 'legacy-frontend', detect: angularJSDetector },
2139
+ { id: 'backbone', name: 'Backbone.js', category: 'legacy-frontend', detect: backboneDetector },
2140
+ { id: 'ember', name: 'Ember.js', category: 'legacy-frontend', detect: emberDetector },
2141
+ { id: 'jquery', name: 'jQuery', category: 'legacy-frontend', detect: jqueryDetector },
2142
+ ];
2143
+
2144
+ /**
2145
+ * Detect Biome in project.
2146
+ *
2147
+ * @param projectPath - Project directory path
2148
+ * @param packageJson - Optional pre-loaded package.json
2149
+ * @returns Detection result or null if not detected
2150
+ *
2151
+ * @example Detecting Biome linter
2152
+ * ```typescript
2153
+ * const result = biomeDetector('/path/to/project', {
2154
+ * devDependencies: { '@biomejs/biome': '^1.5.0' },
2155
+ * })
2156
+ * // => { id: 'biome', name: 'Biome', confidence: 70, version: '1.5.0', ... }
2157
+ * ```
2158
+ */
2159
+ function biomeDetector(projectPath, packageJson) {
2160
+ const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2161
+ const sources = [];
2162
+ let confidence = 0;
2163
+ let configPath;
2164
+ let version;
2165
+ const deps = collectAllDependencies(pkg);
2166
+ if (deps['@biomejs/biome']) {
2167
+ confidence += 70;
2168
+ version = parseVersionString(deps['@biomejs/biome']);
2169
+ sources.push({ type: 'package.json', field: 'dependencies.@biomejs/biome' });
2170
+ }
2171
+ if (exists(join$1(projectPath, 'biome.json'))) {
2172
+ confidence += 30;
2173
+ configPath = 'biome.json';
2174
+ sources.push({ type: 'config-file', path: 'biome.json' });
2256
2175
  }
2257
- if (deps['jquery-validation']) {
2258
- confidence += 5;
2259
- sources.push({ type: 'package.json', field: 'dependencies.jquery-validation' });
2176
+ if (!configPath && exists(join$1(projectPath, 'biome.jsonc'))) {
2177
+ confidence += 30;
2178
+ configPath = 'biome.jsonc';
2179
+ sources.push({ type: 'config-file', path: 'biome.jsonc' });
2260
2180
  }
2261
2181
  if (confidence === 0) {
2262
2182
  return null;
2263
2183
  }
2264
2184
  return {
2265
- id: 'jquery',
2266
- name: 'jQuery',
2267
- category: 'legacy-frontend',
2185
+ id: 'biome',
2186
+ name: 'Biome',
2268
2187
  version,
2188
+ configPath,
2269
2189
  confidence: min(confidence, 100),
2270
2190
  detectedFrom: sources,
2271
2191
  };
2272
2192
  }
2273
2193
 
2274
- /** All legacy framework detectors */
2275
- const legacyDetectors = [
2276
- { id: 'angularjs', name: 'AngularJS', category: 'legacy-frontend', detect: angularJSDetector },
2277
- { id: 'backbone', name: 'Backbone.js', category: 'legacy-frontend', detect: backboneDetector },
2278
- { id: 'ember', name: 'Ember.js', category: 'legacy-frontend', detect: emberDetector },
2279
- { id: 'jquery', name: 'jQuery', category: 'legacy-frontend', detect: jqueryDetector },
2280
- ];
2281
-
2282
2194
  /** Config patterns for ESLint */
2283
2195
  const ESLINT_CONFIG_PATTERNS = [
2284
2196
  'eslint.config.js',
@@ -2298,6 +2210,15 @@ const ESLINT_CONFIG_PATTERNS = [
2298
2210
  * @param projectPath - Project directory path
2299
2211
  * @param packageJson - Optional pre-loaded package.json
2300
2212
  * @returns Detection result or null if not detected
2213
+ *
2214
+ * @example Detecting ESLint linter
2215
+ * ```typescript
2216
+ * const result = eslintDetector('/path/to/project', {
2217
+ * devDependencies: { eslint: '^8.50.0', '@typescript-eslint/parser': '^6.0.0' },
2218
+ * scripts: { lint: 'eslint src/' },
2219
+ * })
2220
+ * // => { id: 'eslint', name: 'ESLint', confidence: 65, version: '8.50.0', ... }
2221
+ * ```
2301
2222
  */
2302
2223
  function eslintDetector(projectPath, packageJson) {
2303
2224
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2361,6 +2282,15 @@ const PRETTIER_CONFIG_PATTERNS = [
2361
2282
  * @param projectPath - Project directory path
2362
2283
  * @param packageJson - Optional pre-loaded package.json
2363
2284
  * @returns Detection result or null if not detected
2285
+ *
2286
+ * @example Detecting Prettier formatter
2287
+ * ```typescript
2288
+ * const result = prettierDetector('/path/to/project', {
2289
+ * devDependencies: { prettier: '^3.0.0' },
2290
+ * scripts: { format: 'prettier --write .' },
2291
+ * })
2292
+ * // => { id: 'prettier', name: 'Prettier', confidence: 55, version: '3.0.0', ... }
2293
+ * ```
2364
2294
  */
2365
2295
  function prettierDetector(projectPath, packageJson) {
2366
2296
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2423,6 +2353,14 @@ const STYLELINT_CONFIG_PATTERNS = [
2423
2353
  * @param projectPath - Project directory path
2424
2354
  * @param packageJson - Optional pre-loaded package.json
2425
2355
  * @returns Detection result or null if not detected
2356
+ *
2357
+ * @example Detecting Stylelint linter
2358
+ * ```typescript
2359
+ * const result = stylelintDetector('/path/to/project', {
2360
+ * devDependencies: { stylelint: '^15.0.0', 'stylelint-config-standard': '^30.0.0' },
2361
+ * })
2362
+ * // => { id: 'stylelint', name: 'Stylelint', confidence: 65, version: '15.0.0', ... }
2363
+ * ```
2426
2364
  */
2427
2365
  function stylelintDetector(projectPath, packageJson) {
2428
2366
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2462,48 +2400,6 @@ function stylelintDetector(projectPath, packageJson) {
2462
2400
  };
2463
2401
  }
2464
2402
 
2465
- /**
2466
- * Detect Biome in project.
2467
- *
2468
- * @param projectPath - Project directory path
2469
- * @param packageJson - Optional pre-loaded package.json
2470
- * @returns Detection result or null if not detected
2471
- */
2472
- function biomeDetector(projectPath, packageJson) {
2473
- const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2474
- const sources = [];
2475
- let confidence = 0;
2476
- let configPath;
2477
- let version;
2478
- const deps = collectAllDependencies(pkg);
2479
- if (deps['@biomejs/biome']) {
2480
- confidence += 70;
2481
- version = parseVersionString(deps['@biomejs/biome']);
2482
- sources.push({ type: 'package.json', field: 'dependencies.@biomejs/biome' });
2483
- }
2484
- if (exists(join$1(projectPath, 'biome.json'))) {
2485
- confidence += 30;
2486
- configPath = 'biome.json';
2487
- sources.push({ type: 'config-file', path: 'biome.json' });
2488
- }
2489
- if (!configPath && exists(join$1(projectPath, 'biome.jsonc'))) {
2490
- confidence += 30;
2491
- configPath = 'biome.jsonc';
2492
- sources.push({ type: 'config-file', path: 'biome.jsonc' });
2493
- }
2494
- if (confidence === 0) {
2495
- return null;
2496
- }
2497
- return {
2498
- id: 'biome',
2499
- name: 'Biome',
2500
- version,
2501
- configPath,
2502
- confidence: min(confidence, 100),
2503
- detectedFrom: sources,
2504
- };
2505
- }
2506
-
2507
2403
  /** All linting tool detectors */
2508
2404
  const lintingDetectors = [
2509
2405
  { id: 'eslint', name: 'ESLint', detect: eslintDetector },
@@ -2513,140 +2409,179 @@ const lintingDetectors = [
2513
2409
  ];
2514
2410
 
2515
2411
  /**
2516
- * Detect NX in project.
2412
+ * Detect Lerna in project.
2517
2413
  *
2518
2414
  * @param workspacePath - Workspace directory path
2519
2415
  * @param packageJson - Optional pre-loaded package.json
2520
2416
  * @returns Detection result or null if not detected
2417
+ *
2418
+ * @example Detecting Lerna monorepo
2419
+ * ```typescript
2420
+ * // Project with lerna.json config file
2421
+ * const result = lernaDetector('/path/to/lerna-project')
2422
+ * // => {
2423
+ * // id: 'lerna',
2424
+ * // name: 'Lerna',
2425
+ * // confidence: 80,
2426
+ * // configPath: 'lerna.json',
2427
+ * // detectedFrom: [{ type: 'config-file', path: 'lerna.json' }]
2428
+ * // }
2429
+ * ```
2521
2430
  */
2522
- function nxDetector(workspacePath, packageJson) {
2431
+ function lernaDetector(workspacePath, packageJson) {
2523
2432
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
2524
2433
  const sources = [];
2525
2434
  let confidence = 0;
2526
2435
  let version;
2527
- let workspaceLayout;
2528
- const nxJsonPath = join$1(workspacePath, 'nx.json');
2529
- if (exists(nxJsonPath)) {
2530
- confidence += 70;
2531
- sources.push({ type: 'config-file', path: 'nx.json' });
2436
+ let configPath;
2437
+ const lernaJsonPath = join$1(workspacePath, 'lerna.json');
2438
+ if (exists(lernaJsonPath)) {
2439
+ confidence += 80;
2440
+ configPath = 'lerna.json';
2441
+ sources.push({ type: 'config-file', path: 'lerna.json' });
2532
2442
  }
2533
2443
  const deps = collectAllDependencies(pkg);
2534
- if (deps['nx']) {
2535
- confidence += 20;
2536
- version = parseVersionString(deps['nx']);
2537
- sources.push({ type: 'package.json', field: 'dependencies.nx' });
2538
- }
2539
- const hasApps = exists(join$1(workspacePath, 'apps'));
2540
- const hasLibs = exists(join$1(workspacePath, 'libs'));
2541
- if (hasApps || hasLibs) {
2542
- confidence += 10;
2543
- sources.push({ type: 'directory', path: 'apps/ or libs/' });
2544
- workspaceLayout = {
2545
- appsDir: hasApps ? 'apps' : '',
2546
- libsDir: hasLibs ? 'libs' : '',
2547
- };
2444
+ if (deps['lerna']) {
2445
+ confidence += 15;
2446
+ version = parseVersionString(deps['lerna']);
2447
+ sources.push({ type: 'package.json', field: 'dependencies.lerna' });
2548
2448
  }
2549
- const nxPackages = keys(deps).filter((d) => d.startsWith('@nx/') || d.startsWith('@nrwl/'));
2550
- if (nxPackages.length > 0) {
2551
- confidence += 10;
2552
- sources.push({ type: 'package.json', field: '@nx/* packages' });
2449
+ if (exists(join$1(workspacePath, 'packages'))) {
2450
+ confidence += 5;
2451
+ sources.push({ type: 'directory', path: 'packages/' });
2553
2452
  }
2554
2453
  if (confidence === 0) {
2555
2454
  return null;
2556
2455
  }
2557
2456
  return {
2558
- id: 'nx',
2559
- name: 'NX',
2457
+ id: 'lerna',
2458
+ name: 'Lerna',
2560
2459
  version,
2561
- configPath: exists(nxJsonPath) ? 'nx.json' : undefined,
2460
+ configPath,
2562
2461
  confidence: min(confidence, 100),
2563
2462
  detectedFrom: sources,
2564
- workspaceLayout,
2565
2463
  };
2566
2464
  }
2567
2465
 
2568
2466
  /**
2569
- * Detect Turborepo in project.
2467
+ * Detect npm workspaces in project.
2570
2468
  *
2571
2469
  * @param workspacePath - Workspace directory path
2572
2470
  * @param packageJson - Optional pre-loaded package.json
2573
2471
  * @returns Detection result or null if not detected
2472
+ *
2473
+ * @example Detecting npm workspaces
2474
+ * ```typescript
2475
+ * // Project with workspaces in package.json and package-lock.json
2476
+ * const result = npmWorkspacesDetector('/path/to/npm-project')
2477
+ * // => {
2478
+ * // id: 'npm-workspaces',
2479
+ * // name: 'npm Workspaces',
2480
+ * // confidence: 90,
2481
+ * // configPath: 'package.json',
2482
+ * // detectedFrom: [
2483
+ * // { type: 'package.json', field: 'workspaces' },
2484
+ * // { type: 'lockfile', path: 'package-lock.json' }
2485
+ * // ]
2486
+ * // }
2487
+ * ```
2574
2488
  */
2575
- function turborepoDetector(workspacePath, packageJson) {
2489
+ function npmWorkspacesDetector(workspacePath, packageJson) {
2576
2490
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
2577
2491
  const sources = [];
2578
2492
  let confidence = 0;
2579
- let version;
2580
- let configPath;
2581
- const turboJsonPath = join$1(workspacePath, 'turbo.json');
2582
- if (exists(turboJsonPath)) {
2493
+ if (pkg?.workspaces) {
2583
2494
  confidence += 80;
2584
- configPath = 'turbo.json';
2585
- sources.push({ type: 'config-file', path: 'turbo.json' });
2495
+ sources.push({ type: 'package.json', field: 'workspaces' });
2586
2496
  }
2587
- const deps = collectAllDependencies(pkg);
2588
- if (deps['turbo']) {
2589
- confidence += 15;
2590
- version = parseVersionString(deps['turbo']);
2591
- sources.push({ type: 'package.json', field: 'dependencies.turbo' });
2497
+ if (exists(join$1(workspacePath, 'package-lock.json'))) {
2498
+ confidence += 10;
2499
+ sources.push({ type: 'lockfile', path: 'package-lock.json' });
2592
2500
  }
2593
- const scripts = pkg?.scripts ?? {};
2594
- if (values(scripts).some((s) => s?.includes('turbo'))) {
2595
- confidence += 5;
2596
- sources.push({ type: 'package.json', field: 'scripts (turbo commands)' });
2501
+ if (exists(join$1(workspacePath, 'yarn.lock'))) {
2502
+ return null;
2597
2503
  }
2598
2504
  if (confidence === 0) {
2599
2505
  return null;
2600
2506
  }
2601
2507
  return {
2602
- id: 'turborepo',
2603
- name: 'Turborepo',
2604
- version,
2605
- configPath,
2508
+ id: 'npm-workspaces',
2509
+ name: 'npm Workspaces',
2510
+ configPath: 'package.json',
2606
2511
  confidence: min(confidence, 100),
2607
2512
  detectedFrom: sources,
2608
2513
  };
2609
2514
  }
2610
2515
 
2611
2516
  /**
2612
- * Detect Lerna in project.
2517
+ * Detect NX in project.
2613
2518
  *
2614
2519
  * @param workspacePath - Workspace directory path
2615
2520
  * @param packageJson - Optional pre-loaded package.json
2616
2521
  * @returns Detection result or null if not detected
2522
+ *
2523
+ * @example Detecting NX workspace
2524
+ * ```typescript
2525
+ * // Project with nx.json and apps/libs directories
2526
+ * const result = nxDetector('/path/to/nx-workspace')
2527
+ * // => {
2528
+ * // id: 'nx',
2529
+ * // name: 'NX',
2530
+ * // confidence: 100,
2531
+ * // configPath: 'nx.json',
2532
+ * // version: '17.0.0',
2533
+ * // workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
2534
+ * // detectedFrom: [
2535
+ * // { type: 'config-file', path: 'nx.json' },
2536
+ * // { type: 'package.json', field: 'dependencies.nx' },
2537
+ * // { type: 'directory', path: 'apps/ or libs/' }
2538
+ * // ]
2539
+ * // }
2540
+ * ```
2617
2541
  */
2618
- function lernaDetector(workspacePath, packageJson) {
2542
+ function nxDetector(workspacePath, packageJson) {
2619
2543
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
2620
2544
  const sources = [];
2621
2545
  let confidence = 0;
2622
2546
  let version;
2623
- let configPath;
2624
- const lernaJsonPath = join$1(workspacePath, 'lerna.json');
2625
- if (exists(lernaJsonPath)) {
2626
- confidence += 80;
2627
- configPath = 'lerna.json';
2628
- sources.push({ type: 'config-file', path: 'lerna.json' });
2547
+ let workspaceLayout;
2548
+ const nxJsonPath = join$1(workspacePath, 'nx.json');
2549
+ if (exists(nxJsonPath)) {
2550
+ confidence += 70;
2551
+ sources.push({ type: 'config-file', path: 'nx.json' });
2629
2552
  }
2630
2553
  const deps = collectAllDependencies(pkg);
2631
- if (deps['lerna']) {
2632
- confidence += 15;
2633
- version = parseVersionString(deps['lerna']);
2634
- sources.push({ type: 'package.json', field: 'dependencies.lerna' });
2554
+ if (deps['nx']) {
2555
+ confidence += 20;
2556
+ version = parseVersionString(deps['nx']);
2557
+ sources.push({ type: 'package.json', field: 'dependencies.nx' });
2635
2558
  }
2636
- if (exists(join$1(workspacePath, 'packages'))) {
2637
- confidence += 5;
2638
- sources.push({ type: 'directory', path: 'packages/' });
2559
+ const hasApps = exists(join$1(workspacePath, 'apps'));
2560
+ const hasLibs = exists(join$1(workspacePath, 'libs'));
2561
+ if (hasApps || hasLibs) {
2562
+ confidence += 10;
2563
+ sources.push({ type: 'directory', path: 'apps/ or libs/' });
2564
+ workspaceLayout = {
2565
+ appsDir: hasApps ? 'apps' : '',
2566
+ libsDir: hasLibs ? 'libs' : '',
2567
+ };
2568
+ }
2569
+ const nxPackages = keys(deps).filter((d) => d.startsWith('@nx/') || d.startsWith('@nrwl/'));
2570
+ if (nxPackages.length > 0) {
2571
+ confidence += 10;
2572
+ sources.push({ type: 'package.json', field: '@nx/* packages' });
2639
2573
  }
2640
2574
  if (confidence === 0) {
2641
2575
  return null;
2642
2576
  }
2643
2577
  return {
2644
- id: 'lerna',
2645
- name: 'Lerna',
2578
+ id: 'nx',
2579
+ name: 'NX',
2646
2580
  version,
2647
- configPath,
2581
+ configPath: exists(nxJsonPath) ? 'nx.json' : undefined,
2648
2582
  confidence: min(confidence, 100),
2649
2583
  detectedFrom: sources,
2584
+ workspaceLayout,
2650
2585
  };
2651
2586
  }
2652
2587
 
@@ -2656,6 +2591,19 @@ function lernaDetector(workspacePath, packageJson) {
2656
2591
  * @param workspacePath - Workspace directory path
2657
2592
  * @param packageJson - Optional pre-loaded package.json
2658
2593
  * @returns Detection result or null if not detected
2594
+ *
2595
+ * @example Detecting Rush monorepo
2596
+ * ```typescript
2597
+ * // Project with rush.json config file
2598
+ * const result = rushDetector('/path/to/rush-project')
2599
+ * // => {
2600
+ * // id: 'rush',
2601
+ * // name: 'Rush',
2602
+ * // confidence: 90,
2603
+ * // configPath: 'rush.json',
2604
+ * // detectedFrom: [{ type: 'config-file', path: 'rush.json' }]
2605
+ * // }
2606
+ * ```
2659
2607
  */
2660
2608
  function rushDetector(workspacePath, packageJson) {
2661
2609
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
@@ -2670,54 +2618,22 @@ function rushDetector(workspacePath, packageJson) {
2670
2618
  sources.push({ type: 'config-file', path: 'rush.json' });
2671
2619
  }
2672
2620
  const deps = collectAllDependencies(pkg);
2673
- if (deps['@microsoft/rush']) {
2674
- confidence += 10;
2675
- version = parseVersionString(deps['@microsoft/rush']);
2676
- sources.push({ type: 'package.json', field: 'dependencies.@microsoft/rush' });
2677
- }
2678
- if (exists(join$1(workspacePath, 'common', 'config', 'rush'))) {
2679
- confidence += 5;
2680
- sources.push({ type: 'directory', path: 'common/config/rush/' });
2681
- }
2682
- if (confidence === 0) {
2683
- return null;
2684
- }
2685
- return {
2686
- id: 'rush',
2687
- name: 'Rush',
2688
- version,
2689
- configPath,
2690
- confidence: min(confidence, 100),
2691
- detectedFrom: sources,
2692
- };
2693
- }
2694
-
2695
- /**
2696
- * Detect pnpm workspaces in project.
2697
- *
2698
- * @param workspacePath - Workspace directory path
2699
- * @returns Detection result or null if not detected
2700
- */
2701
- function pnpmWorkspacesDetector(workspacePath) {
2702
- const sources = [];
2703
- let confidence = 0;
2704
- let configPath;
2705
- const pnpmWorkspacePath = join$1(workspacePath, 'pnpm-workspace.yaml');
2706
- if (exists(pnpmWorkspacePath)) {
2707
- confidence += 90;
2708
- configPath = 'pnpm-workspace.yaml';
2709
- sources.push({ type: 'config-file', path: 'pnpm-workspace.yaml' });
2710
- }
2711
- if (exists(join$1(workspacePath, 'pnpm-lock.yaml'))) {
2621
+ if (deps['@microsoft/rush']) {
2712
2622
  confidence += 10;
2713
- sources.push({ type: 'lockfile', path: 'pnpm-lock.yaml' });
2623
+ version = parseVersionString(deps['@microsoft/rush']);
2624
+ sources.push({ type: 'package.json', field: 'dependencies.@microsoft/rush' });
2625
+ }
2626
+ if (exists(join$1(workspacePath, 'common', 'config', 'rush'))) {
2627
+ confidence += 5;
2628
+ sources.push({ type: 'directory', path: 'common/config/rush/' });
2714
2629
  }
2715
2630
  if (confidence === 0) {
2716
2631
  return null;
2717
2632
  }
2718
2633
  return {
2719
- id: 'pnpm-workspaces',
2720
- name: 'pnpm Workspaces',
2634
+ id: 'rush',
2635
+ name: 'Rush',
2636
+ version,
2721
2637
  configPath,
2722
2638
  confidence: min(confidence, 100),
2723
2639
  detectedFrom: sources,
@@ -2725,34 +2641,60 @@ function pnpmWorkspacesDetector(workspacePath) {
2725
2641
  }
2726
2642
 
2727
2643
  /**
2728
- * Detect npm workspaces in project.
2644
+ * Detect Turborepo in project.
2729
2645
  *
2730
2646
  * @param workspacePath - Workspace directory path
2731
2647
  * @param packageJson - Optional pre-loaded package.json
2732
2648
  * @returns Detection result or null if not detected
2649
+ *
2650
+ * @example Detecting Turborepo monorepo
2651
+ * ```typescript
2652
+ * // Project with turbo.json and turbo dependency
2653
+ * const result = turborepoDetector('/path/to/turbo-project')
2654
+ * // => {
2655
+ * // id: 'turborepo',
2656
+ * // name: 'Turborepo',
2657
+ * // confidence: 95,
2658
+ * // configPath: 'turbo.json',
2659
+ * // version: '2.0.0',
2660
+ * // detectedFrom: [
2661
+ * // { type: 'config-file', path: 'turbo.json' },
2662
+ * // { type: 'package.json', field: 'dependencies.turbo' }
2663
+ * // ]
2664
+ * // }
2665
+ * ```
2733
2666
  */
2734
- function npmWorkspacesDetector(workspacePath, packageJson) {
2667
+ function turborepoDetector(workspacePath, packageJson) {
2735
2668
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
2736
2669
  const sources = [];
2737
2670
  let confidence = 0;
2738
- if (pkg?.workspaces) {
2671
+ let version;
2672
+ let configPath;
2673
+ const turboJsonPath = join$1(workspacePath, 'turbo.json');
2674
+ if (exists(turboJsonPath)) {
2739
2675
  confidence += 80;
2740
- sources.push({ type: 'package.json', field: 'workspaces' });
2676
+ configPath = 'turbo.json';
2677
+ sources.push({ type: 'config-file', path: 'turbo.json' });
2741
2678
  }
2742
- if (exists(join$1(workspacePath, 'package-lock.json'))) {
2743
- confidence += 10;
2744
- sources.push({ type: 'lockfile', path: 'package-lock.json' });
2679
+ const deps = collectAllDependencies(pkg);
2680
+ if (deps['turbo']) {
2681
+ confidence += 15;
2682
+ version = parseVersionString(deps['turbo']);
2683
+ sources.push({ type: 'package.json', field: 'dependencies.turbo' });
2745
2684
  }
2746
- if (exists(join$1(workspacePath, 'yarn.lock'))) {
2747
- return null;
2685
+ const scripts = pkg?.scripts ?? {};
2686
+ if (values(scripts).some((s) => s?.includes('turbo'))) {
2687
+ confidence += 5;
2688
+ sources.push({ type: 'package.json', field: 'scripts (turbo commands)' });
2748
2689
  }
2749
2690
  if (confidence === 0) {
2750
2691
  return null;
2751
2692
  }
2752
2693
  return {
2753
- id: 'npm-workspaces',
2754
- name: 'npm Workspaces',
2755
- configPath: 'package.json',
2694
+ id: 'turborepo',
2695
+ name: 'Turborepo',
2696
+ version,
2697
+ configPath,
2756
2698
  confidence: min(confidence, 100),
2757
2699
  detectedFrom: sources,
2758
2700
  };
@@ -2764,6 +2706,23 @@ function npmWorkspacesDetector(workspacePath, packageJson) {
2764
2706
  * @param workspacePath - Workspace directory path
2765
2707
  * @param packageJson - Optional pre-loaded package.json
2766
2708
  * @returns Detection result or null if not detected
2709
+ *
2710
+ * @example Detecting yarn workspaces
2711
+ * ```typescript
2712
+ * // Project with workspaces in package.json and yarn.lock
2713
+ * const result = yarnWorkspacesDetector('/path/to/yarn-project')
2714
+ * // => {
2715
+ * // id: 'yarn-workspaces',
2716
+ * // name: 'Yarn Workspaces',
2717
+ * // confidence: 100,
2718
+ * // configPath: 'package.json',
2719
+ * // detectedFrom: [
2720
+ * // { type: 'package.json', field: 'workspaces' },
2721
+ * // { type: 'lockfile', path: 'yarn.lock' },
2722
+ * // { type: 'config-file', path: '.yarnrc.yml' }
2723
+ * // ]
2724
+ * // }
2725
+ * ```
2767
2726
  */
2768
2727
  function yarnWorkspacesDetector(workspacePath, packageJson) {
2769
2728
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
@@ -2804,55 +2763,58 @@ const monorepoDetectors = [
2804
2763
  { id: 'yarn-workspaces', name: 'Yarn Workspaces', detect: yarnWorkspacesDetector },
2805
2764
  ];
2806
2765
 
2807
- /** Config patterns for Jest */
2808
- const JEST_CONFIG_PATTERNS = ['jest.config.js', 'jest.config.ts', 'jest.config.mjs', 'jest.config.cjs', 'jest.config.json'];
2766
+ /** Config patterns for Cypress */
2767
+ const CYPRESS_CONFIG_PATTERNS = ['cypress.config.js', 'cypress.config.ts', 'cypress.config.mjs', 'cypress.json'];
2809
2768
  /**
2810
- * Detect Jest in project.
2769
+ * Detect Cypress in project.
2811
2770
  *
2812
2771
  * @param projectPath - Project directory path
2813
2772
  * @param packageJson - Optional pre-loaded package.json
2814
2773
  * @returns Detection result or null if not detected
2774
+ *
2775
+ * @example Detecting Cypress testing framework
2776
+ * ```typescript
2777
+ * import { cypressDetector } from '@hyperfrontend/project-scope'
2778
+ *
2779
+ * const result = cypressDetector('./my-project')
2780
+ * if (result) {
2781
+ * console.log(`Cypress ${result.version} detected (${result.confidence}% confidence)`)
2782
+ * // => "Cypress 13.6.0 detected (95% confidence)"
2783
+ * }
2784
+ * ```
2815
2785
  */
2816
- function jestDetector(projectPath, packageJson) {
2786
+ function cypressDetector(projectPath, packageJson) {
2817
2787
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2818
2788
  const sources = [];
2819
2789
  let confidence = 0;
2820
2790
  let version;
2821
2791
  const deps = collectAllDependencies(pkg);
2822
- if (deps['jest']) {
2792
+ if (deps['cypress']) {
2823
2793
  confidence += 60;
2824
- version = parseVersionString(deps['jest']);
2825
- sources.push({ type: 'package.json', field: 'dependencies.jest' });
2794
+ version = parseVersionString(deps['cypress']);
2795
+ sources.push({ type: 'package.json', field: 'dependencies.cypress' });
2826
2796
  }
2827
- const configPath = locateConfigFile(projectPath, JEST_CONFIG_PATTERNS);
2797
+ const configPath = locateConfigFile(projectPath, CYPRESS_CONFIG_PATTERNS);
2828
2798
  if (configPath) {
2829
2799
  confidence += 30;
2830
2800
  sources.push({ type: 'config-file', path: configPath });
2831
2801
  }
2832
- if (pkg && 'jest' in pkg) {
2833
- confidence += 20;
2834
- sources.push({ type: 'package.json', field: 'jest' });
2835
- }
2836
- const testScript = pkg?.scripts?.['test'] ?? '';
2837
- if (testScript.includes('jest')) {
2802
+ if (exists(join$1(projectPath, 'cypress'))) {
2838
2803
  confidence += 10;
2839
- sources.push({ type: 'package.json', field: 'scripts.test' });
2840
- }
2841
- if (deps['@types/jest']) {
2842
- confidence += 5;
2843
- sources.push({ type: 'package.json', field: 'dependencies.@types/jest' });
2804
+ sources.push({ type: 'directory', path: 'cypress/' });
2844
2805
  }
2845
- if (deps['ts-jest']) {
2806
+ const e2eScript = pkg?.scripts?.['e2e'] ?? pkg?.scripts?.['test:e2e'] ?? '';
2807
+ if (e2eScript.includes('cypress')) {
2846
2808
  confidence += 5;
2847
- sources.push({ type: 'package.json', field: 'dependencies.ts-jest' });
2809
+ sources.push({ type: 'package.json', field: 'scripts.e2e or scripts.test:e2e' });
2848
2810
  }
2849
2811
  if (confidence === 0) {
2850
2812
  return null;
2851
2813
  }
2852
2814
  return {
2853
- id: 'jest',
2854
- name: 'Jest',
2855
- type: 'unit',
2815
+ id: 'cypress',
2816
+ name: 'Cypress',
2817
+ type: 'e2e',
2856
2818
  version,
2857
2819
  configPath,
2858
2820
  confidence: min(confidence, 100),
@@ -2860,51 +2822,66 @@ function jestDetector(projectPath, packageJson) {
2860
2822
  };
2861
2823
  }
2862
2824
 
2863
- /** Config patterns for Vitest */
2864
- const VITEST_CONFIG_PATTERNS = ['vitest.config.js', 'vitest.config.ts', 'vitest.config.mjs'];
2825
+ /** Config patterns for Jest */
2826
+ const JEST_CONFIG_PATTERNS = ['jest.config.js', 'jest.config.ts', 'jest.config.mjs', 'jest.config.cjs', 'jest.config.json'];
2865
2827
  /**
2866
- * Detect Vitest in project.
2828
+ * Detect Jest in project.
2867
2829
  *
2868
2830
  * @param projectPath - Project directory path
2869
2831
  * @param packageJson - Optional pre-loaded package.json
2870
2832
  * @returns Detection result or null if not detected
2833
+ *
2834
+ * @example Detecting Jest testing framework
2835
+ * ```typescript
2836
+ * import { jestDetector } from '@hyperfrontend/project-scope'
2837
+ *
2838
+ * const result = jestDetector('./my-project')
2839
+ * if (result) {
2840
+ * console.log(`Jest ${result.version} detected`)
2841
+ * console.log('Sources:', result.detectedFrom.map(s => s.type))
2842
+ * // => "Sources: ['package.json', 'config-file']"
2843
+ * }
2844
+ * ```
2871
2845
  */
2872
- function vitestDetector(projectPath, packageJson) {
2846
+ function jestDetector(projectPath, packageJson) {
2873
2847
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2874
2848
  const sources = [];
2875
2849
  let confidence = 0;
2876
2850
  let version;
2877
2851
  const deps = collectAllDependencies(pkg);
2878
- if (deps['vitest']) {
2879
- confidence += 70;
2880
- version = parseVersionString(deps['vitest']);
2881
- sources.push({ type: 'package.json', field: 'dependencies.vitest' });
2852
+ if (deps['jest']) {
2853
+ confidence += 60;
2854
+ version = parseVersionString(deps['jest']);
2855
+ sources.push({ type: 'package.json', field: 'dependencies.jest' });
2882
2856
  }
2883
- const configPath = locateConfigFile(projectPath, VITEST_CONFIG_PATTERNS);
2857
+ const configPath = locateConfigFile(projectPath, JEST_CONFIG_PATTERNS);
2884
2858
  if (configPath) {
2885
- confidence += 25;
2859
+ confidence += 30;
2886
2860
  sources.push({ type: 'config-file', path: configPath });
2887
2861
  }
2888
- if (!configPath) {
2889
- const viteConfig = exists(join$1(projectPath, 'vite.config.ts')) ||
2890
- exists(join$1(projectPath, 'vite.config.js')) ||
2891
- exists(join$1(projectPath, 'vite.config.mjs'));
2892
- if (viteConfig && deps['vitest']) {
2893
- confidence += 5;
2894
- sources.push({ type: 'config-file', path: 'vite.config.*' });
2895
- }
2862
+ if (pkg && 'jest' in pkg) {
2863
+ confidence += 20;
2864
+ sources.push({ type: 'package.json', field: 'jest' });
2896
2865
  }
2897
2866
  const testScript = pkg?.scripts?.['test'] ?? '';
2898
- if (testScript.includes('vitest')) {
2867
+ if (testScript.includes('jest')) {
2899
2868
  confidence += 10;
2900
2869
  sources.push({ type: 'package.json', field: 'scripts.test' });
2901
2870
  }
2871
+ if (deps['@types/jest']) {
2872
+ confidence += 5;
2873
+ sources.push({ type: 'package.json', field: 'dependencies.@types/jest' });
2874
+ }
2875
+ if (deps['ts-jest']) {
2876
+ confidence += 5;
2877
+ sources.push({ type: 'package.json', field: 'dependencies.ts-jest' });
2878
+ }
2902
2879
  if (confidence === 0) {
2903
2880
  return null;
2904
2881
  }
2905
2882
  return {
2906
- id: 'vitest',
2907
- name: 'Vitest',
2883
+ id: 'jest',
2884
+ name: 'Jest',
2908
2885
  type: 'unit',
2909
2886
  version,
2910
2887
  configPath,
@@ -2921,6 +2898,17 @@ const MOCHA_CONFIG_PATTERNS = ['.mocharc.js', '.mocharc.json', '.mocharc.yaml',
2921
2898
  * @param projectPath - Project directory path
2922
2899
  * @param packageJson - Optional pre-loaded package.json
2923
2900
  * @returns Detection result or null if not detected
2901
+ *
2902
+ * @example Detecting Mocha testing framework
2903
+ * ```typescript
2904
+ * import { mochaDetector } from '@hyperfrontend/project-scope'
2905
+ *
2906
+ * const result = mochaDetector('./my-project')
2907
+ * if (result) {
2908
+ * console.log(`Mocha ${result.version} detected (${result.confidence}%)`)
2909
+ * // => "Mocha 10.2.0 detected (95%)"
2910
+ * }
2911
+ * ```
2924
2912
  */
2925
2913
  function mochaDetector(projectPath, packageJson) {
2926
2914
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2965,37 +2953,53 @@ function mochaDetector(projectPath, packageJson) {
2965
2953
  };
2966
2954
  }
2967
2955
 
2968
- /** Config patterns for Cypress */
2969
- const CYPRESS_CONFIG_PATTERNS = ['cypress.config.js', 'cypress.config.ts', 'cypress.config.mjs', 'cypress.json'];
2956
+ /** Config patterns for Playwright */
2957
+ const PLAYWRIGHT_CONFIG_PATTERNS = ['playwright.config.js', 'playwright.config.ts', 'playwright.config.mjs'];
2970
2958
  /**
2971
- * Detect Cypress in project.
2959
+ * Detect Playwright in project.
2972
2960
  *
2973
2961
  * @param projectPath - Project directory path
2974
2962
  * @param packageJson - Optional pre-loaded package.json
2975
2963
  * @returns Detection result or null if not detected
2964
+ *
2965
+ * @example Detecting Playwright testing framework
2966
+ * ```typescript
2967
+ * import { playwrightDetector } from '@hyperfrontend/project-scope'
2968
+ *
2969
+ * const result = playwrightDetector('./my-project')
2970
+ * if (result) {
2971
+ * console.log(`Playwright ${result.version} (${result.type} tests)`)
2972
+ * // => "Playwright 1.42.0 (e2e tests)"
2973
+ * }
2974
+ * ```
2976
2975
  */
2977
- function cypressDetector(projectPath, packageJson) {
2976
+ function playwrightDetector(projectPath, packageJson) {
2978
2977
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2979
2978
  const sources = [];
2980
2979
  let confidence = 0;
2981
2980
  let version;
2982
2981
  const deps = collectAllDependencies(pkg);
2983
- if (deps['cypress']) {
2984
- confidence += 60;
2985
- version = parseVersionString(deps['cypress']);
2986
- sources.push({ type: 'package.json', field: 'dependencies.cypress' });
2982
+ if (deps['@playwright/test']) {
2983
+ confidence += 70;
2984
+ version = parseVersionString(deps['@playwright/test']);
2985
+ sources.push({ type: 'package.json', field: 'dependencies.@playwright/test' });
2987
2986
  }
2988
- const configPath = locateConfigFile(projectPath, CYPRESS_CONFIG_PATTERNS);
2987
+ if (deps['playwright']) {
2988
+ confidence += 50;
2989
+ version = version ?? parseVersionString(deps['playwright']);
2990
+ sources.push({ type: 'package.json', field: 'dependencies.playwright' });
2991
+ }
2992
+ const configPath = locateConfigFile(projectPath, PLAYWRIGHT_CONFIG_PATTERNS);
2989
2993
  if (configPath) {
2990
- confidence += 30;
2994
+ confidence += 25;
2991
2995
  sources.push({ type: 'config-file', path: configPath });
2992
2996
  }
2993
- if (exists(join$1(projectPath, 'cypress'))) {
2994
- confidence += 10;
2995
- sources.push({ type: 'directory', path: 'cypress/' });
2997
+ if (exists(join$1(projectPath, 'e2e')) || exists(join$1(projectPath, 'tests'))) {
2998
+ confidence += 5;
2999
+ sources.push({ type: 'directory', path: 'e2e/ or tests/' });
2996
3000
  }
2997
3001
  const e2eScript = pkg?.scripts?.['e2e'] ?? pkg?.scripts?.['test:e2e'] ?? '';
2998
- if (e2eScript.includes('cypress')) {
3002
+ if (e2eScript.includes('playwright')) {
2999
3003
  confidence += 5;
3000
3004
  sources.push({ type: 'package.json', field: 'scripts.e2e or scripts.test:e2e' });
3001
3005
  }
@@ -3003,8 +3007,8 @@ function cypressDetector(projectPath, packageJson) {
3003
3007
  return null;
3004
3008
  }
3005
3009
  return {
3006
- id: 'cypress',
3007
- name: 'Cypress',
3010
+ id: 'playwright',
3011
+ name: 'Playwright',
3008
3012
  type: 'e2e',
3009
3013
  version,
3010
3014
  configPath,
@@ -3013,52 +3017,64 @@ function cypressDetector(projectPath, packageJson) {
3013
3017
  };
3014
3018
  }
3015
3019
 
3016
- /** Config patterns for Playwright */
3017
- const PLAYWRIGHT_CONFIG_PATTERNS = ['playwright.config.js', 'playwright.config.ts', 'playwright.config.mjs'];
3020
+ /** Config patterns for Vitest */
3021
+ const VITEST_CONFIG_PATTERNS = ['vitest.config.js', 'vitest.config.ts', 'vitest.config.mjs'];
3018
3022
  /**
3019
- * Detect Playwright in project.
3023
+ * Detect Vitest in project.
3020
3024
  *
3021
3025
  * @param projectPath - Project directory path
3022
3026
  * @param packageJson - Optional pre-loaded package.json
3023
3027
  * @returns Detection result or null if not detected
3028
+ *
3029
+ * @example Detecting Vitest testing framework
3030
+ * ```typescript
3031
+ * import { vitestDetector } from '@hyperfrontend/project-scope'
3032
+ *
3033
+ * const result = vitestDetector('./my-project')
3034
+ * if (result) {
3035
+ * console.log(`Vitest ${result.version} detected`)
3036
+ * console.log('Config:', result.configPath)
3037
+ * // => "Config: vitest.config.ts"
3038
+ * }
3039
+ * ```
3024
3040
  */
3025
- function playwrightDetector(projectPath, packageJson) {
3041
+ function vitestDetector(projectPath, packageJson) {
3026
3042
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
3027
3043
  const sources = [];
3028
3044
  let confidence = 0;
3029
3045
  let version;
3030
3046
  const deps = collectAllDependencies(pkg);
3031
- if (deps['@playwright/test']) {
3047
+ if (deps['vitest']) {
3032
3048
  confidence += 70;
3033
- version = parseVersionString(deps['@playwright/test']);
3034
- sources.push({ type: 'package.json', field: 'dependencies.@playwright/test' });
3035
- }
3036
- if (deps['playwright']) {
3037
- confidence += 50;
3038
- version = version ?? parseVersionString(deps['playwright']);
3039
- sources.push({ type: 'package.json', field: 'dependencies.playwright' });
3049
+ version = parseVersionString(deps['vitest']);
3050
+ sources.push({ type: 'package.json', field: 'dependencies.vitest' });
3040
3051
  }
3041
- const configPath = locateConfigFile(projectPath, PLAYWRIGHT_CONFIG_PATTERNS);
3052
+ const configPath = locateConfigFile(projectPath, VITEST_CONFIG_PATTERNS);
3042
3053
  if (configPath) {
3043
3054
  confidence += 25;
3044
3055
  sources.push({ type: 'config-file', path: configPath });
3045
3056
  }
3046
- if (exists(join$1(projectPath, 'e2e')) || exists(join$1(projectPath, 'tests'))) {
3047
- confidence += 5;
3048
- sources.push({ type: 'directory', path: 'e2e/ or tests/' });
3057
+ if (!configPath) {
3058
+ const viteConfig = exists(join$1(projectPath, 'vite.config.ts')) ||
3059
+ exists(join$1(projectPath, 'vite.config.js')) ||
3060
+ exists(join$1(projectPath, 'vite.config.mjs'));
3061
+ if (viteConfig && deps['vitest']) {
3062
+ confidence += 5;
3063
+ sources.push({ type: 'config-file', path: 'vite.config.*' });
3064
+ }
3049
3065
  }
3050
- const e2eScript = pkg?.scripts?.['e2e'] ?? pkg?.scripts?.['test:e2e'] ?? '';
3051
- if (e2eScript.includes('playwright')) {
3052
- confidence += 5;
3053
- sources.push({ type: 'package.json', field: 'scripts.e2e or scripts.test:e2e' });
3066
+ const testScript = pkg?.scripts?.['test'] ?? '';
3067
+ if (testScript.includes('vitest')) {
3068
+ confidence += 10;
3069
+ sources.push({ type: 'package.json', field: 'scripts.test' });
3054
3070
  }
3055
3071
  if (confidence === 0) {
3056
3072
  return null;
3057
3073
  }
3058
3074
  return {
3059
- id: 'playwright',
3060
- name: 'Playwright',
3061
- type: 'e2e',
3075
+ id: 'vitest',
3076
+ name: 'Vitest',
3077
+ type: 'unit',
3062
3078
  version,
3063
3079
  configPath,
3064
3080
  confidence: min(confidence, 100),
@@ -3101,6 +3117,19 @@ function checkTsConfigStrict(projectPath) {
3101
3117
  * @param projectPath - Project directory path
3102
3118
  * @param packageJson - Optional pre-loaded package.json
3103
3119
  * @returns Detection result or null if not detected
3120
+ *
3121
+ * @example Detecting TypeScript
3122
+ * ```typescript
3123
+ * import { typescriptDetector } from '@hyperfrontend/project-scope'
3124
+ *
3125
+ * const result = typescriptDetector('./my-project')
3126
+ * if (result) {
3127
+ * console.log(`TypeScript ${result.version}`)
3128
+ * console.log(`Strict mode: ${result.strictMode ?? 'unknown'}`)
3129
+ * // => "TypeScript 5.3.0"
3130
+ * // => "Strict mode: true"
3131
+ * }
3132
+ * ```
3104
3133
  */
3105
3134
  function typescriptDetector(projectPath, packageJson) {
3106
3135
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -3152,6 +3181,17 @@ function typescriptDetector(projectPath, packageJson) {
3152
3181
  * @param projectPath - Project directory path
3153
3182
  * @param packageJson - Optional pre-loaded package.json
3154
3183
  * @returns Detection result or null if not detected
3184
+ *
3185
+ * @example Detecting Flow type system
3186
+ * ```typescript
3187
+ * import { flowDetector } from '@hyperfrontend/project-scope'
3188
+ *
3189
+ * const result = flowDetector('./my-project')
3190
+ * if (result) {
3191
+ * console.log(`Flow ${result.version} with config: ${result.configPath}`)
3192
+ * // => "Flow 0.232.0 with config: .flowconfig"
3193
+ * }
3194
+ * ```
3155
3195
  */
3156
3196
  function flowDetector(projectPath, packageJson) {
3157
3197
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -3209,6 +3249,18 @@ function hasJsDocTypes(content) {
3209
3249
  * @param projectPath - Project directory path
3210
3250
  * @param packageJson - Optional pre-loaded package.json
3211
3251
  * @returns Detection result or null if not detected
3252
+ *
3253
+ * @example Detecting JSDoc type annotations
3254
+ * ```typescript
3255
+ * import { jsdocDetector } from '@hyperfrontend/project-scope'
3256
+ *
3257
+ * const result = jsdocDetector('./my-project')
3258
+ * if (result) {
3259
+ * console.log('JSDoc types detected')
3260
+ * console.log('Sources:', result.detectedFrom.map(s => s.path ?? s.field))
3261
+ * // => "Sources: ['jsconfig.json', 'src/utils.js (JSDoc annotations)']"
3262
+ * }
3263
+ * ```
3212
3264
  */
3213
3265
  function jsdocDetector(projectPath, packageJson) {
3214
3266
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -3302,7 +3354,7 @@ function isDetectAllOptions(value) {
3302
3354
  * @param packageJsonOrOptions - Optional pre-loaded package.json or options object
3303
3355
  * @returns All detection results organized by category
3304
3356
  *
3305
- * @example
3357
+ * @example Running all tech detectors
3306
3358
  * ```typescript
3307
3359
  * import { detectAll } from '@hyperfrontend/project-scope'
3308
3360
  *
@@ -3394,7 +3446,7 @@ const projectTypeLogger = createScopedLogger('project-scope:heuristics:project-t
3394
3446
  * @param options - Detection options
3395
3447
  * @returns Project type detection result with confidence score
3396
3448
  *
3397
- * @example
3449
+ * @example Detecting project type
3398
3450
  * ```typescript
3399
3451
  * import { detectProjectType } from '@hyperfrontend/project-scope'
3400
3452
  *
@@ -3566,4 +3618,3 @@ function detectProjectType(projectPath, options) {
3566
3618
  }
3567
3619
 
3568
3620
  export { detectProjectType };
3569
- //# sourceMappingURL=index.esm.js.map