@hyperfrontend/project-scope 0.2.0 → 0.2.2

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