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