@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
package/tech/index.esm.js CHANGED
@@ -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
  };
@@ -1216,6 +675,19 @@ const backendDetectors = [
1216
675
  * @param projectPath - Project directory path
1217
676
  * @param packageJson - Optional pre-loaded package.json
1218
677
  * @returns Array of detected frameworks, sorted by confidence
678
+ * @example Detecting multiple backend frameworks
679
+ * ```typescript
680
+ * const pkg = {
681
+ * dependencies: { '@nestjs/core': '^10.0.0', '@nestjs/common': '^10.0.0' },
682
+ * devDependencies: { express: '^4.18.0' },
683
+ * }
684
+ *
685
+ * const results = detectBackendFrameworks('/path/to/project', pkg)
686
+ * // => [
687
+ * // { id: 'nestjs', name: 'NestJS', confidence: 85, ... },
688
+ * // { id: 'express', name: 'Express', confidence: 80, ... },
689
+ * // ]
690
+ * ```
1219
691
  */
1220
692
  function detectBackendFrameworks(projectPath, packageJson) {
1221
693
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -1229,52 +701,64 @@ function detectBackendFrameworks(projectPath, packageJson) {
1229
701
  return results.sort((a, b) => b.confidence - a.confidence);
1230
702
  }
1231
703
 
1232
- /** Config patterns for Webpack */
1233
- const WEBPACK_CONFIG_PATTERNS = [
1234
- 'webpack.config.js',
1235
- 'webpack.config.ts',
1236
- 'webpack.config.cjs',
1237
- 'webpack.config.mjs',
1238
- 'webpack.config.babel.js',
1239
- ];
704
+ /** Config patterns for Babel */
705
+ const BABEL_CONFIG_PATTERNS = ['babel.config.js', 'babel.config.cjs', 'babel.config.mjs', 'babel.config.json', '.babelrc', '.babelrc.json', '.babelrc.js'];
1240
706
  /**
1241
- * Detect Webpack in project.
707
+ * Detect Babel in project.
1242
708
  *
1243
709
  * @param projectPath - Project directory path
1244
710
  * @param packageJson - Optional pre-loaded package.json
1245
711
  * @returns Detection result or null if not detected
712
+ *
713
+ * @example Detecting Babel compiler
714
+ * ```typescript
715
+ * const result = babelDetector('/path/to/project', {
716
+ * name: 'my-app',
717
+ * devDependencies: { '@babel/core': '^7.23.0', '@babel/preset-env': '^7.23.0' }
718
+ * })
719
+ * // => {
720
+ * // id: 'babel',
721
+ * // name: 'Babel',
722
+ * // version: '7.23.0',
723
+ * // confidence: 60,
724
+ * // detectedFrom: [
725
+ * // { type: 'package.json', field: 'dependencies.@babel/core' },
726
+ * // { type: 'package.json', field: 'dependencies (@babel packages)' }
727
+ * // ]
728
+ * // }
729
+ * ```
1246
730
  */
1247
- function webpackDetector(projectPath, packageJson) {
731
+ function babelDetector(projectPath, packageJson) {
1248
732
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1249
733
  const sources = [];
1250
734
  let confidence = 0;
1251
735
  let version;
1252
736
  const deps = collectAllDependencies(pkg);
1253
- if (deps['webpack']) {
737
+ if (deps['@babel/core']) {
1254
738
  confidence += 50;
1255
- version = parseVersionString(deps['webpack']);
1256
- sources.push({ type: 'package.json', field: 'dependencies.webpack' });
739
+ version = parseVersionString(deps['@babel/core']);
740
+ sources.push({ type: 'package.json', field: 'dependencies.@babel/core' });
1257
741
  }
1258
- const configPath = locateConfigFile(projectPath, WEBPACK_CONFIG_PATTERNS);
742
+ const configPath = locateConfigFile(projectPath, BABEL_CONFIG_PATTERNS);
1259
743
  if (configPath) {
1260
744
  confidence += 40;
1261
745
  sources.push({ type: 'config-file', path: configPath });
1262
746
  }
1263
- if (deps['webpack-cli']) {
1264
- confidence += 10;
1265
- sources.push({ type: 'package.json', field: 'dependencies.webpack-cli' });
747
+ if (pkg && 'babel' in pkg) {
748
+ confidence += 30;
749
+ sources.push({ type: 'package.json', field: 'babel' });
1266
750
  }
1267
- const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'webpack');
1268
- for (const name of scriptMatches) {
1269
- confidence = min(confidence + 5, 100);
1270
- sources.push({ type: 'package.json', field: `scripts.${name}` });
751
+ const babelPackages = keys(deps).filter((d) => d.startsWith('@babel/'));
752
+ if (babelPackages.length > 1) {
753
+ confidence += 10;
754
+ sources.push({ type: 'package.json', field: 'dependencies (@babel packages)' });
1271
755
  }
1272
756
  if (confidence === 0) {
1273
757
  return null;
1274
758
  }
1275
759
  return {
1276
- id: 'webpack',
1277
- name: 'Webpack',
760
+ id: 'babel',
761
+ name: 'Babel',
1278
762
  version,
1279
763
  configPath,
1280
764
  confidence: min(confidence, 100),
@@ -1282,94 +766,126 @@ function webpackDetector(projectPath, packageJson) {
1282
766
  };
1283
767
  }
1284
768
 
1285
- /** Config patterns for Vite */
1286
- const VITE_CONFIG_PATTERNS = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.cjs'];
1287
769
  /**
1288
- * Detect Vite in project.
770
+ * Detect esbuild in project.
1289
771
  *
1290
772
  * @param projectPath - Project directory path
1291
773
  * @param packageJson - Optional pre-loaded package.json
1292
774
  * @returns Detection result or null if not detected
775
+ *
776
+ * @example Detecting esbuild bundler
777
+ * ```typescript
778
+ * const result = esbuildDetector('/path/to/project', {
779
+ * name: 'my-lib',
780
+ * devDependencies: { 'esbuild': '^0.19.0' },
781
+ * scripts: { 'build': 'esbuild src/index.ts --bundle --outfile=dist/index.js' }
782
+ * })
783
+ * // => {
784
+ * // id: 'esbuild',
785
+ * // name: 'esbuild',
786
+ * // version: '0.19.0',
787
+ * // confidence: 80,
788
+ * // detectedFrom: [
789
+ * // { type: 'package.json', field: 'dependencies.esbuild' },
790
+ * // { type: 'package.json', field: 'scripts.build' }
791
+ * // ]
792
+ * // }
793
+ * ```
1293
794
  */
1294
- function viteDetector(projectPath, packageJson) {
795
+ function esbuildDetector(projectPath, packageJson) {
1295
796
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1296
797
  const sources = [];
1297
798
  let confidence = 0;
1298
799
  let version;
1299
800
  const deps = collectAllDependencies(pkg);
1300
- if (deps['vite']) {
1301
- confidence += 60;
1302
- version = parseVersionString(deps['vite']);
1303
- sources.push({ type: 'package.json', field: 'dependencies.vite' });
1304
- }
1305
- const configPath = locateConfigFile(projectPath, VITE_CONFIG_PATTERNS);
1306
- if (configPath) {
1307
- confidence += 35;
1308
- sources.push({ type: 'config-file', path: configPath });
801
+ if (deps['esbuild']) {
802
+ confidence += 70;
803
+ version = parseVersionString(deps['esbuild']);
804
+ sources.push({ type: 'package.json', field: 'dependencies.esbuild' });
1309
805
  }
1310
- if (deps['vitest']) {
1311
- confidence += 10;
1312
- sources.push({ type: 'package.json', field: 'dependencies.vitest' });
806
+ const esbuildPlugins = keys(deps).filter((d) => d.includes('esbuild-plugin') || d.includes('esbuild-'));
807
+ if (esbuildPlugins.length > 0) {
808
+ confidence += 15;
809
+ sources.push({ type: 'package.json', field: 'dependencies (esbuild plugins)' });
1313
810
  }
1314
- const vitePlugins = keys(deps).filter((d) => d.startsWith('vite-plugin-') || d.startsWith('@vitejs/'));
1315
- if (vitePlugins.length > 0) {
1316
- confidence += 10;
1317
- sources.push({ type: 'package.json', field: 'dependencies (vite plugins)' });
811
+ const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'esbuild');
812
+ for (const name of scriptMatches) {
813
+ confidence = min(confidence + 10, 100);
814
+ sources.push({ type: 'package.json', field: `scripts.${name}` });
1318
815
  }
1319
816
  if (confidence === 0) {
1320
817
  return null;
1321
818
  }
1322
819
  return {
1323
- id: 'vite',
1324
- name: 'Vite',
820
+ id: 'esbuild',
821
+ name: 'esbuild',
1325
822
  version,
1326
- configPath,
1327
823
  confidence: min(confidence, 100),
1328
824
  detectedFrom: sources,
1329
825
  };
1330
826
  }
1331
827
 
1332
- /** Config patterns for Rollup */
1333
- const ROLLUP_CONFIG_PATTERNS = ['rollup.config.js', 'rollup.config.ts', 'rollup.config.mjs', 'rollup.config.cjs'];
828
+ /** Config patterns for Parcel */
829
+ const PARCEL_CONFIG_PATTERNS = ['.parcelrc'];
1334
830
  /**
1335
- * Detect Rollup in project.
831
+ * Detect Parcel in project.
1336
832
  *
1337
833
  * @param projectPath - Project directory path
1338
834
  * @param packageJson - Optional pre-loaded package.json
1339
835
  * @returns Detection result or null if not detected
836
+ *
837
+ * @example Detecting Parcel bundler
838
+ * ```typescript
839
+ * const result = parcelDetector('/path/to/project', {
840
+ * name: 'my-app',
841
+ * devDependencies: { 'parcel': '^2.10.0' },
842
+ * scripts: { 'dev': 'parcel src/index.html', 'build': 'parcel build src/index.html' }
843
+ * })
844
+ * // => {
845
+ * // id: 'parcel',
846
+ * // name: 'Parcel',
847
+ * // version: '2.10.0',
848
+ * // confidence: 80,
849
+ * // detectedFrom: [
850
+ * // { type: 'package.json', field: 'dependencies.parcel' },
851
+ * // { type: 'package.json', field: 'scripts.dev' },
852
+ * // { type: 'package.json', field: 'scripts.build' }
853
+ * // ]
854
+ * // }
855
+ * ```
1340
856
  */
1341
- function rollupDetector(projectPath, packageJson) {
857
+ function parcelDetector(projectPath, packageJson) {
1342
858
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1343
859
  const sources = [];
1344
860
  let confidence = 0;
1345
861
  let version;
1346
862
  const deps = collectAllDependencies(pkg);
1347
- if (deps['rollup']) {
1348
- confidence += 55;
1349
- version = parseVersionString(deps['rollup']);
1350
- sources.push({ type: 'package.json', field: 'dependencies.rollup' });
863
+ if (deps['parcel']) {
864
+ confidence += 60;
865
+ version = parseVersionString(deps['parcel']);
866
+ sources.push({ type: 'package.json', field: 'dependencies.parcel' });
1351
867
  }
1352
- const configPath = locateConfigFile(projectPath, ROLLUP_CONFIG_PATTERNS);
868
+ if (deps['parcel-bundler']) {
869
+ confidence += 60;
870
+ version = parseVersionString(deps['parcel-bundler']);
871
+ sources.push({ type: 'package.json', field: 'dependencies.parcel-bundler' });
872
+ }
873
+ const configPath = locateConfigFile(projectPath, PARCEL_CONFIG_PATTERNS);
1353
874
  if (configPath) {
1354
- confidence += 40;
875
+ confidence += 30;
1355
876
  sources.push({ type: 'config-file', path: configPath });
1356
877
  }
1357
- const rollupPlugins = keys(deps).filter((d) => d.startsWith('@rollup/') || d.startsWith('rollup-plugin-'));
1358
- if (rollupPlugins.length > 0) {
1359
- confidence += 10;
1360
- sources.push({ type: 'package.json', field: 'dependencies (rollup plugins)' });
1361
- }
1362
- const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'rollup');
878
+ const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'parcel');
1363
879
  for (const name of scriptMatches) {
1364
- confidence = min(confidence + 5, 100);
880
+ confidence = min(confidence + 10, 100);
1365
881
  sources.push({ type: 'package.json', field: `scripts.${name}` });
1366
882
  }
1367
883
  if (confidence === 0) {
1368
884
  return null;
1369
885
  }
1370
886
  return {
1371
- id: 'rollup',
1372
- name: 'Rollup',
887
+ id: 'parcel',
888
+ name: 'Parcel',
1373
889
  version,
1374
890
  configPath,
1375
891
  confidence: min(confidence, 100),
@@ -1377,86 +893,134 @@ function rollupDetector(projectPath, packageJson) {
1377
893
  };
1378
894
  }
1379
895
 
896
+ /** Config patterns for Rollup */
897
+ const ROLLUP_CONFIG_PATTERNS = ['rollup.config.js', 'rollup.config.ts', 'rollup.config.mjs', 'rollup.config.cjs'];
1380
898
  /**
1381
- * Detect esbuild in project.
899
+ * Detect Rollup in project.
1382
900
  *
1383
901
  * @param projectPath - Project directory path
1384
902
  * @param packageJson - Optional pre-loaded package.json
1385
903
  * @returns Detection result or null if not detected
904
+ *
905
+ * @example Detecting Rollup bundler
906
+ * ```typescript
907
+ * const result = rollupDetector('/path/to/project', {
908
+ * name: 'my-lib',
909
+ * devDependencies: {
910
+ * 'rollup': '^4.0.0',
911
+ * '@rollup/plugin-node-resolve': '^15.0.0',
912
+ * '@rollup/plugin-commonjs': '^25.0.0'
913
+ * }
914
+ * })
915
+ * // => {
916
+ * // id: 'rollup',
917
+ * // name: 'Rollup',
918
+ * // version: '4.0.0',
919
+ * // confidence: 65,
920
+ * // detectedFrom: [
921
+ * // { type: 'package.json', field: 'dependencies.rollup' },
922
+ * // { type: 'package.json', field: 'dependencies (rollup plugins)' }
923
+ * // ]
924
+ * // }
925
+ * ```
1386
926
  */
1387
- function esbuildDetector(projectPath, packageJson) {
927
+ function rollupDetector(projectPath, packageJson) {
1388
928
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1389
929
  const sources = [];
1390
930
  let confidence = 0;
1391
931
  let version;
1392
932
  const deps = collectAllDependencies(pkg);
1393
- if (deps['esbuild']) {
1394
- confidence += 70;
1395
- version = parseVersionString(deps['esbuild']);
1396
- sources.push({ type: 'package.json', field: 'dependencies.esbuild' });
933
+ if (deps['rollup']) {
934
+ confidence += 55;
935
+ version = parseVersionString(deps['rollup']);
936
+ sources.push({ type: 'package.json', field: 'dependencies.rollup' });
1397
937
  }
1398
- const esbuildPlugins = keys(deps).filter((d) => d.includes('esbuild-plugin') || d.includes('esbuild-'));
1399
- if (esbuildPlugins.length > 0) {
1400
- confidence += 15;
1401
- sources.push({ type: 'package.json', field: 'dependencies (esbuild plugins)' });
938
+ const configPath = locateConfigFile(projectPath, ROLLUP_CONFIG_PATTERNS);
939
+ if (configPath) {
940
+ confidence += 40;
941
+ sources.push({ type: 'config-file', path: configPath });
1402
942
  }
1403
- const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'esbuild');
943
+ const rollupPlugins = keys(deps).filter((d) => d.startsWith('@rollup/') || d.startsWith('rollup-plugin-'));
944
+ if (rollupPlugins.length > 0) {
945
+ confidence += 10;
946
+ sources.push({ type: 'package.json', field: 'dependencies (rollup plugins)' });
947
+ }
948
+ const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'rollup');
1404
949
  for (const name of scriptMatches) {
1405
- confidence = min(confidence + 10, 100);
950
+ confidence = min(confidence + 5, 100);
1406
951
  sources.push({ type: 'package.json', field: `scripts.${name}` });
1407
952
  }
1408
953
  if (confidence === 0) {
1409
954
  return null;
1410
955
  }
1411
956
  return {
1412
- id: 'esbuild',
1413
- name: 'esbuild',
957
+ id: 'rollup',
958
+ name: 'Rollup',
1414
959
  version,
960
+ configPath,
1415
961
  confidence: min(confidence, 100),
1416
962
  detectedFrom: sources,
1417
963
  };
1418
964
  }
1419
965
 
1420
- /** Config patterns for Babel */
1421
- const BABEL_CONFIG_PATTERNS = ['babel.config.js', 'babel.config.cjs', 'babel.config.mjs', 'babel.config.json', '.babelrc', '.babelrc.json', '.babelrc.js'];
966
+ /** Config patterns for SWC */
967
+ const SWC_CONFIG_PATTERNS = ['.swcrc', 'swc.config.js'];
1422
968
  /**
1423
- * Detect Babel in project.
969
+ * Detect SWC in project.
1424
970
  *
1425
971
  * @param projectPath - Project directory path
1426
972
  * @param packageJson - Optional pre-loaded package.json
1427
973
  * @returns Detection result or null if not detected
974
+ *
975
+ * @example Detecting SWC compiler
976
+ * ```typescript
977
+ * const result = swcDetector('/path/to/project', {
978
+ * name: 'my-app',
979
+ * devDependencies: { '@swc/core': '^1.3.0', '@swc/cli': '^0.1.0' }
980
+ * })
981
+ * // => {
982
+ * // id: 'swc',
983
+ * // name: 'SWC',
984
+ * // version: '1.3.0',
985
+ * // confidence: 70,
986
+ * // detectedFrom: [
987
+ * // { type: 'package.json', field: 'dependencies.@swc/core' },
988
+ * // { type: 'package.json', field: 'dependencies.@swc/cli' }
989
+ * // ]
990
+ * // }
991
+ * ```
1428
992
  */
1429
- function babelDetector(projectPath, packageJson) {
993
+ function swcDetector(projectPath, packageJson) {
1430
994
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1431
995
  const sources = [];
1432
996
  let confidence = 0;
1433
997
  let version;
1434
998
  const deps = collectAllDependencies(pkg);
1435
- if (deps['@babel/core']) {
1436
- confidence += 50;
1437
- version = parseVersionString(deps['@babel/core']);
1438
- sources.push({ type: 'package.json', field: 'dependencies.@babel/core' });
999
+ if (deps['@swc/core']) {
1000
+ confidence += 60;
1001
+ version = parseVersionString(deps['@swc/core']);
1002
+ sources.push({ type: 'package.json', field: 'dependencies.@swc/core' });
1439
1003
  }
1440
- const configPath = locateConfigFile(projectPath, BABEL_CONFIG_PATTERNS);
1004
+ const configPath = locateConfigFile(projectPath, SWC_CONFIG_PATTERNS);
1441
1005
  if (configPath) {
1442
- confidence += 40;
1006
+ confidence += 35;
1443
1007
  sources.push({ type: 'config-file', path: configPath });
1444
1008
  }
1445
- if (pkg && 'babel' in pkg) {
1446
- confidence += 30;
1447
- sources.push({ type: 'package.json', field: 'babel' });
1448
- }
1449
- const babelPackages = keys(deps).filter((d) => d.startsWith('@babel/'));
1450
- if (babelPackages.length > 1) {
1009
+ if (deps['@swc/cli']) {
1451
1010
  confidence += 10;
1452
- sources.push({ type: 'package.json', field: 'dependencies (@babel packages)' });
1011
+ sources.push({ type: 'package.json', field: 'dependencies.@swc/cli' });
1012
+ }
1013
+ const swcPlugins = keys(deps).filter((d) => d.startsWith('@swc/') || d.includes('swc-plugin'));
1014
+ if (swcPlugins.length > 1) {
1015
+ confidence += 5;
1016
+ sources.push({ type: 'package.json', field: 'dependencies (@swc packages)' });
1453
1017
  }
1454
1018
  if (confidence === 0) {
1455
1019
  return null;
1456
1020
  }
1457
1021
  return {
1458
- id: 'babel',
1459
- name: 'Babel',
1022
+ id: 'swc',
1023
+ name: 'SWC',
1460
1024
  version,
1461
1025
  configPath,
1462
1026
  confidence: min(confidence, 100),
@@ -1464,46 +1028,69 @@ function babelDetector(projectPath, packageJson) {
1464
1028
  };
1465
1029
  }
1466
1030
 
1467
- /** Config patterns for SWC */
1468
- const SWC_CONFIG_PATTERNS = ['.swcrc', 'swc.config.js'];
1031
+ /** Config patterns for Vite */
1032
+ const VITE_CONFIG_PATTERNS = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.cjs'];
1469
1033
  /**
1470
- * Detect SWC in project.
1034
+ * Detect Vite in project.
1471
1035
  *
1472
1036
  * @param projectPath - Project directory path
1473
1037
  * @param packageJson - Optional pre-loaded package.json
1474
1038
  * @returns Detection result or null if not detected
1039
+ *
1040
+ * @example Detecting Vite build tool
1041
+ * ```typescript
1042
+ * const result = viteDetector('/path/to/project', {
1043
+ * name: 'my-app',
1044
+ * devDependencies: {
1045
+ * 'vite': '^5.0.0',
1046
+ * '@vitejs/plugin-react': '^4.0.0',
1047
+ * 'vitest': '^1.0.0'
1048
+ * }
1049
+ * })
1050
+ * // => {
1051
+ * // id: 'vite',
1052
+ * // name: 'Vite',
1053
+ * // version: '5.0.0',
1054
+ * // confidence: 80,
1055
+ * // detectedFrom: [
1056
+ * // { type: 'package.json', field: 'dependencies.vite' },
1057
+ * // { type: 'package.json', field: 'dependencies.vitest' },
1058
+ * // { type: 'package.json', field: 'dependencies (vite plugins)' }
1059
+ * // ]
1060
+ * // }
1061
+ * ```
1475
1062
  */
1476
- function swcDetector(projectPath, packageJson) {
1063
+ function viteDetector(projectPath, packageJson) {
1477
1064
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1478
1065
  const sources = [];
1479
1066
  let confidence = 0;
1480
1067
  let version;
1481
1068
  const deps = collectAllDependencies(pkg);
1482
- if (deps['@swc/core']) {
1069
+ if (deps['vite']) {
1483
1070
  confidence += 60;
1484
- version = parseVersionString(deps['@swc/core']);
1485
- sources.push({ type: 'package.json', field: 'dependencies.@swc/core' });
1071
+ version = parseVersionString(deps['vite']);
1072
+ sources.push({ type: 'package.json', field: 'dependencies.vite' });
1486
1073
  }
1487
- const configPath = locateConfigFile(projectPath, SWC_CONFIG_PATTERNS);
1074
+ const configPath = locateConfigFile(projectPath, VITE_CONFIG_PATTERNS);
1488
1075
  if (configPath) {
1489
1076
  confidence += 35;
1490
1077
  sources.push({ type: 'config-file', path: configPath });
1491
1078
  }
1492
- if (deps['@swc/cli']) {
1079
+ if (deps['vitest']) {
1493
1080
  confidence += 10;
1494
- sources.push({ type: 'package.json', field: 'dependencies.@swc/cli' });
1081
+ sources.push({ type: 'package.json', field: 'dependencies.vitest' });
1495
1082
  }
1496
- const swcPlugins = keys(deps).filter((d) => d.startsWith('@swc/') || d.includes('swc-plugin'));
1497
- if (swcPlugins.length > 1) {
1498
- confidence += 5;
1499
- sources.push({ type: 'package.json', field: 'dependencies (@swc packages)' });
1083
+ const vitePlugins = keys(deps).filter((d) => d.startsWith('vite-plugin-') || d.startsWith('@vitejs/'));
1084
+ if (vitePlugins.length > 0) {
1085
+ confidence += 10;
1086
+ sources.push({ type: 'package.json', field: 'dependencies (vite plugins)' });
1500
1087
  }
1501
1088
  if (confidence === 0) {
1502
1089
  return null;
1503
1090
  }
1504
1091
  return {
1505
- id: 'swc',
1506
- name: 'SWC',
1092
+ id: 'vite',
1093
+ name: 'Vite',
1507
1094
  version,
1508
1095
  configPath,
1509
1096
  confidence: min(confidence, 100),
@@ -1511,47 +1098,72 @@ function swcDetector(projectPath, packageJson) {
1511
1098
  };
1512
1099
  }
1513
1100
 
1514
- /** Config patterns for Parcel */
1515
- const PARCEL_CONFIG_PATTERNS = ['.parcelrc'];
1101
+ /** Config patterns for Webpack */
1102
+ const WEBPACK_CONFIG_PATTERNS = [
1103
+ 'webpack.config.js',
1104
+ 'webpack.config.ts',
1105
+ 'webpack.config.cjs',
1106
+ 'webpack.config.mjs',
1107
+ 'webpack.config.babel.js',
1108
+ ];
1516
1109
  /**
1517
- * Detect Parcel in project.
1110
+ * Detect Webpack in project.
1518
1111
  *
1519
1112
  * @param projectPath - Project directory path
1520
1113
  * @param packageJson - Optional pre-loaded package.json
1521
1114
  * @returns Detection result or null if not detected
1115
+ *
1116
+ * @example Detecting Webpack bundler
1117
+ * ```typescript
1118
+ * const result = webpackDetector('/path/to/project', {
1119
+ * name: 'my-app',
1120
+ * devDependencies: { 'webpack': '^5.89.0', 'webpack-cli': '^5.1.0' },
1121
+ * scripts: { 'build': 'webpack --mode production' }
1122
+ * })
1123
+ * // => {
1124
+ * // id: 'webpack',
1125
+ * // name: 'Webpack',
1126
+ * // version: '5.89.0',
1127
+ * // confidence: 65,
1128
+ * // detectedFrom: [
1129
+ * // { type: 'package.json', field: 'dependencies.webpack' },
1130
+ * // { type: 'package.json', field: 'dependencies.webpack-cli' },
1131
+ * // { type: 'package.json', field: 'scripts.build' }
1132
+ * // ]
1133
+ * // }
1134
+ * ```
1522
1135
  */
1523
- function parcelDetector(projectPath, packageJson) {
1136
+ function webpackDetector(projectPath, packageJson) {
1524
1137
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1525
1138
  const sources = [];
1526
1139
  let confidence = 0;
1527
1140
  let version;
1528
1141
  const deps = collectAllDependencies(pkg);
1529
- if (deps['parcel']) {
1530
- confidence += 60;
1531
- version = parseVersionString(deps['parcel']);
1532
- sources.push({ type: 'package.json', field: 'dependencies.parcel' });
1533
- }
1534
- if (deps['parcel-bundler']) {
1535
- confidence += 60;
1536
- version = parseVersionString(deps['parcel-bundler']);
1537
- sources.push({ type: 'package.json', field: 'dependencies.parcel-bundler' });
1142
+ if (deps['webpack']) {
1143
+ confidence += 50;
1144
+ version = parseVersionString(deps['webpack']);
1145
+ sources.push({ type: 'package.json', field: 'dependencies.webpack' });
1538
1146
  }
1539
- const configPath = locateConfigFile(projectPath, PARCEL_CONFIG_PATTERNS);
1147
+ const configPath = locateConfigFile(projectPath, WEBPACK_CONFIG_PATTERNS);
1540
1148
  if (configPath) {
1541
- confidence += 30;
1149
+ confidence += 40;
1542
1150
  sources.push({ type: 'config-file', path: configPath });
1543
1151
  }
1544
- const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'parcel');
1152
+ if (deps['webpack-cli']) {
1153
+ confidence += 10;
1154
+ sources.push({ type: 'package.json', field: 'dependencies.webpack-cli' });
1155
+ }
1156
+ const scriptMatches = filterScriptsByCommand(pkg?.scripts, 'webpack');
1545
1157
  for (const name of scriptMatches) {
1546
- confidence = min(confidence + 10, 100);
1158
+ confidence = min(confidence + 5, 100);
1547
1159
  sources.push({ type: 'package.json', field: `scripts.${name}` });
1548
1160
  }
1549
1161
  if (confidence === 0) {
1550
1162
  return null;
1551
1163
  }
1552
1164
  return {
1553
- id: 'parcel',
1554
- name: 'Parcel',
1165
+ id: 'webpack',
1166
+ name: 'Webpack',
1555
1167
  version,
1556
1168
  configPath,
1557
1169
  confidence: min(confidence, 100),
@@ -1575,6 +1187,22 @@ const buildToolDetectors = [
1575
1187
  * @param projectPath - Project directory path
1576
1188
  * @param packageJson - Optional pre-loaded package.json
1577
1189
  * @returns Array of detected build tools, sorted by confidence
1190
+ *
1191
+ * @example Detecting multiple build tools
1192
+ * ```typescript
1193
+ * const tools = detectBuildTools('/path/to/project', {
1194
+ * name: 'my-app',
1195
+ * devDependencies: {
1196
+ * 'vite': '^5.0.0',
1197
+ * '@vitejs/plugin-react': '^4.0.0',
1198
+ * '@babel/core': '^7.23.0'
1199
+ * }
1200
+ * })
1201
+ * // => [
1202
+ * // { id: 'vite', name: 'Vite', version: '5.0.0', confidence: 70, ... },
1203
+ * // { id: 'babel', name: 'Babel', version: '7.23.0', confidence: 50, ... }
1204
+ * // ]
1205
+ * ```
1578
1206
  */
1579
1207
  function detectBuildTools(projectPath, packageJson) {
1580
1208
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -1589,81 +1217,187 @@ function detectBuildTools(projectPath, packageJson) {
1589
1217
  }
1590
1218
 
1591
1219
  /**
1592
- * Detect React in project.
1220
+ * Detect Angular in project.
1221
+ *
1222
+ * @param projectPath - Project directory path
1223
+ * @param packageJson - Optional pre-loaded package.json
1224
+ * @returns Detection result or null if not detected
1225
+ *
1226
+ * @example Detecting Angular framework
1227
+ * ```typescript
1228
+ * const result = angularDetector('/path/to/angular-app', {
1229
+ * dependencies: { '@angular/core': '^17.0.0', '@angular/cli': '^17.0.0' }
1230
+ * })
1231
+ * // => {
1232
+ * // id: 'angular',
1233
+ * // name: 'Angular',
1234
+ * // category: 'frontend',
1235
+ * // version: '17.0.0',
1236
+ * // confidence: 85,
1237
+ * // detectedFrom: [
1238
+ * // { type: 'package.json', field: 'dependencies.@angular/core' },
1239
+ * // { type: 'package.json', field: 'dependencies.@angular/cli' }
1240
+ * // ]
1241
+ * // }
1242
+ * ```
1243
+ */
1244
+ function angularDetector(projectPath, packageJson) {
1245
+ const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1246
+ const sources = [];
1247
+ let confidence = 0;
1248
+ let version;
1249
+ const deps = collectAllDependencies(pkg);
1250
+ if (deps['@angular/core']) {
1251
+ confidence += 70;
1252
+ version = parseVersionString(deps['@angular/core']);
1253
+ sources.push({ type: 'package.json', field: 'dependencies.@angular/core' });
1254
+ }
1255
+ if (deps['@angular/cli']) {
1256
+ confidence += 15;
1257
+ sources.push({ type: 'package.json', field: 'dependencies.@angular/cli' });
1258
+ }
1259
+ if (exists(join$1(projectPath, 'angular.json'))) {
1260
+ confidence += 15;
1261
+ sources.push({ type: 'config-file', path: 'angular.json' });
1262
+ }
1263
+ if (deps['angular'] && !deps['@angular/core']) {
1264
+ return {
1265
+ id: 'angularjs',
1266
+ name: 'AngularJS (Legacy)',
1267
+ category: 'frontend',
1268
+ version: parseVersionString(deps['angular']),
1269
+ confidence: 80,
1270
+ detectedFrom: [{ type: 'package.json', field: 'dependencies.angular' }],
1271
+ };
1272
+ }
1273
+ if (confidence === 0) {
1274
+ return null;
1275
+ }
1276
+ return {
1277
+ id: 'angular',
1278
+ name: 'Angular',
1279
+ category: 'frontend',
1280
+ version,
1281
+ confidence: min(confidence, 100),
1282
+ detectedFrom: sources,
1283
+ };
1284
+ }
1285
+
1286
+ /**
1287
+ * Detect Astro in project.
1288
+ *
1289
+ * @param projectPath - Project directory path
1290
+ * @param packageJson - Optional pre-loaded package.json
1291
+ * @returns Detection result or null if not detected
1292
+ *
1293
+ * @example Detecting Astro framework
1294
+ * ```typescript
1295
+ * const result = astroDetector('/path/to/astro-project', {
1296
+ * dependencies: { 'astro': '^4.0.0' }
1297
+ * })
1298
+ * // => {
1299
+ * // id: 'astro',
1300
+ * // name: 'Astro',
1301
+ * // category: 'meta-framework',
1302
+ * // version: '4.0.0',
1303
+ * // confidence: 70,
1304
+ * // detectedFrom: [{ type: 'package.json', field: 'dependencies.astro' }]
1305
+ * // }
1306
+ * ```
1307
+ */
1308
+ function astroDetector(projectPath, packageJson) {
1309
+ const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1310
+ const sources = [];
1311
+ let confidence = 0;
1312
+ let version;
1313
+ const deps = collectAllDependencies(pkg);
1314
+ if (deps['astro']) {
1315
+ confidence += 70;
1316
+ version = parseVersionString(deps['astro']);
1317
+ sources.push({ type: 'package.json', field: 'dependencies.astro' });
1318
+ }
1319
+ if (exists(join$1(projectPath, 'astro.config.mjs')) ||
1320
+ exists(join$1(projectPath, 'astro.config.ts')) ||
1321
+ exists(join$1(projectPath, 'astro.config.js'))) {
1322
+ confidence += 25;
1323
+ sources.push({ type: 'config-file', path: 'astro.config.*' });
1324
+ }
1325
+ if (exists(join$1(projectPath, 'src', 'pages'))) {
1326
+ confidence += 5;
1327
+ sources.push({ type: 'directory', path: 'src/pages/' });
1328
+ }
1329
+ if (confidence === 0) {
1330
+ return null;
1331
+ }
1332
+ return {
1333
+ id: 'astro',
1334
+ name: 'Astro',
1335
+ category: 'meta-framework',
1336
+ version,
1337
+ confidence: min(confidence, 100),
1338
+ detectedFrom: sources,
1339
+ };
1340
+ }
1341
+
1342
+ /**
1343
+ * Detect Gatsby in project.
1593
1344
  *
1594
1345
  * @param projectPath - Project directory path
1595
1346
  * @param packageJson - Optional pre-loaded package.json
1596
1347
  * @returns Detection result or null if not detected
1348
+ *
1349
+ * @example Detecting Gatsby framework
1350
+ * ```typescript
1351
+ * const result = gatsbyDetector('/path/to/gatsby-blog', {
1352
+ * dependencies: {
1353
+ * 'gatsby': '^5.0.0',
1354
+ * 'gatsby-plugin-image': '^3.0.0',
1355
+ * 'gatsby-source-filesystem': '^5.0.0'
1356
+ * }
1357
+ * })
1358
+ * // => {
1359
+ * // id: 'gatsby',
1360
+ * // name: 'Gatsby',
1361
+ * // category: 'meta-framework',
1362
+ * // version: '5.0.0',
1363
+ * // confidence: 75,
1364
+ * // detectedFrom: [
1365
+ * // { type: 'package.json', field: 'dependencies.gatsby' },
1366
+ * // { type: 'package.json', field: 'dependencies (gatsby plugins)' }
1367
+ * // ]
1368
+ * // }
1369
+ * ```
1597
1370
  */
1598
- function reactDetector(projectPath, packageJson) {
1371
+ function gatsbyDetector(projectPath, packageJson) {
1599
1372
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1600
1373
  const sources = [];
1601
1374
  let confidence = 0;
1602
1375
  let version;
1603
- const metaFrameworks = [];
1604
1376
  const deps = collectAllDependencies(pkg);
1605
- if (deps['react']) {
1606
- confidence += 60;
1607
- version = parseVersionString(deps['react']);
1608
- sources.push({ type: 'package.json', field: 'dependencies.react' });
1609
- }
1610
- if (deps['react-dom']) {
1611
- confidence += 20;
1612
- sources.push({ type: 'package.json', field: 'dependencies.react-dom' });
1613
- }
1614
- if (deps['react-native']) {
1615
- confidence += 20;
1616
- sources.push({ type: 'package.json', field: 'dependencies.react-native' });
1617
- }
1618
- const hasJsxFiles = exists(join$1(projectPath, 'src', 'App.tsx')) ||
1619
- exists(join$1(projectPath, 'src', 'App.jsx')) ||
1620
- exists(join$1(projectPath, 'src', 'index.tsx')) ||
1621
- exists(join$1(projectPath, 'src', 'index.jsx'));
1622
- if (hasJsxFiles) {
1623
- confidence += 10;
1624
- sources.push({ type: 'directory', path: 'src/*.tsx or src/*.jsx' });
1625
- }
1626
- if (deps['next']) {
1627
- metaFrameworks.push({
1628
- id: 'nextjs',
1629
- name: 'Next.js',
1630
- category: 'meta-framework',
1631
- version: parseVersionString(deps['next']),
1632
- confidence: 90,
1633
- detectedFrom: [{ type: 'package.json', field: 'dependencies.next' }],
1634
- });
1635
- }
1636
1377
  if (deps['gatsby']) {
1637
- metaFrameworks.push({
1638
- id: 'gatsby',
1639
- name: 'Gatsby',
1640
- category: 'meta-framework',
1641
- version: parseVersionString(deps['gatsby']),
1642
- confidence: 90,
1643
- detectedFrom: [{ type: 'package.json', field: 'dependencies.gatsby' }],
1644
- });
1378
+ confidence += 70;
1379
+ version = parseVersionString(deps['gatsby']);
1380
+ sources.push({ type: 'package.json', field: 'dependencies.gatsby' });
1645
1381
  }
1646
- if (deps['@remix-run/react'] || deps['remix']) {
1647
- metaFrameworks.push({
1648
- id: 'remix',
1649
- name: 'Remix',
1650
- category: 'meta-framework',
1651
- version: parseVersionString(deps['@remix-run/react'] ?? deps['remix']),
1652
- confidence: 90,
1653
- detectedFrom: [{ type: 'package.json', field: 'dependencies.@remix-run/react' }],
1654
- });
1382
+ if (exists(join$1(projectPath, 'gatsby-config.js')) || exists(join$1(projectPath, 'gatsby-config.ts'))) {
1383
+ confidence += 25;
1384
+ sources.push({ type: 'config-file', path: 'gatsby-config.*' });
1385
+ }
1386
+ const gatsbyPlugins = keys(deps).filter((d) => d.startsWith('gatsby-plugin-') || d.startsWith('gatsby-source-'));
1387
+ if (gatsbyPlugins.length > 0) {
1388
+ confidence += 5;
1389
+ sources.push({ type: 'package.json', field: 'dependencies (gatsby plugins)' });
1655
1390
  }
1656
1391
  if (confidence === 0) {
1657
1392
  return null;
1658
1393
  }
1659
1394
  return {
1660
- id: 'react',
1661
- name: 'React',
1662
- category: 'frontend',
1395
+ id: 'gatsby',
1396
+ name: 'Gatsby',
1397
+ category: 'meta-framework',
1663
1398
  version,
1664
1399
  confidence: min(confidence, 100),
1665
1400
  detectedFrom: sources,
1666
- metaFrameworks: metaFrameworks.length > 0 ? metaFrameworks : undefined,
1667
1401
  };
1668
1402
  }
1669
1403
 
@@ -1673,6 +1407,21 @@ function reactDetector(projectPath, packageJson) {
1673
1407
  * @param projectPath - Project directory path
1674
1408
  * @param packageJson - Optional pre-loaded package.json
1675
1409
  * @returns Detection result or null if not detected
1410
+ *
1411
+ * @example Detecting Next.js framework
1412
+ * ```typescript
1413
+ * const result = nextjsDetector('/path/to/nextjs-app', {
1414
+ * dependencies: { 'next': '^14.0.0', 'react': '^18.0.0' }
1415
+ * })
1416
+ * // => {
1417
+ * // id: 'nextjs',
1418
+ * // name: 'Next.js',
1419
+ * // category: 'meta-framework',
1420
+ * // version: '14.0.0',
1421
+ * // confidence: 70,
1422
+ * // detectedFrom: [{ type: 'package.json', field: 'dependencies.next' }]
1423
+ * // }
1424
+ * ```
1676
1425
  */
1677
1426
  function nextjsDetector(projectPath, packageJson) {
1678
1427
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -1712,38 +1461,52 @@ function nextjsDetector(projectPath, packageJson) {
1712
1461
  }
1713
1462
 
1714
1463
  /**
1715
- * Detect Remix in project.
1464
+ * Detect Nuxt in project.
1716
1465
  *
1717
1466
  * @param projectPath - Project directory path
1718
1467
  * @param packageJson - Optional pre-loaded package.json
1719
1468
  * @returns Detection result or null if not detected
1469
+ *
1470
+ * @example Detecting Nuxt framework
1471
+ * ```typescript
1472
+ * const result = nuxtDetector('/path/to/nuxt-app', {
1473
+ * dependencies: { 'nuxt': '^3.0.0', 'vue': '^3.0.0' }
1474
+ * })
1475
+ * // => {
1476
+ * // id: 'nuxt',
1477
+ * // name: 'Nuxt',
1478
+ * // category: 'meta-framework',
1479
+ * // version: '3.0.0',
1480
+ * // confidence: 70,
1481
+ * // detectedFrom: [{ type: 'package.json', field: 'dependencies.nuxt' }]
1482
+ * // }
1483
+ * ```
1720
1484
  */
1721
- function remixDetector(projectPath, packageJson) {
1485
+ function nuxtDetector(projectPath, packageJson) {
1722
1486
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1723
1487
  const sources = [];
1724
1488
  let confidence = 0;
1725
1489
  let version;
1726
1490
  const deps = collectAllDependencies(pkg);
1727
- // @remix-run packages
1728
- if (deps['@remix-run/react']) {
1491
+ if (deps['nuxt'] || deps['nuxt3']) {
1729
1492
  confidence += 70;
1730
- version = parseVersionString(deps['@remix-run/react']);
1731
- sources.push({ type: 'package.json', field: 'dependencies.@remix-run/react' });
1493
+ version = parseVersionString(deps['nuxt'] ?? deps['nuxt3']);
1494
+ sources.push({ type: 'package.json', field: 'dependencies.nuxt' });
1732
1495
  }
1733
- if (deps['@remix-run/node'] || deps['@remix-run/cloudflare'] || deps['@remix-run/deno']) {
1734
- confidence += 20;
1735
- sources.push({ type: 'package.json', field: 'dependencies.@remix-run/*' });
1496
+ if (exists(join$1(projectPath, 'nuxt.config.js')) || exists(join$1(projectPath, 'nuxt.config.ts'))) {
1497
+ confidence += 25;
1498
+ sources.push({ type: 'config-file', path: 'nuxt.config.*' });
1736
1499
  }
1737
- if (exists(join$1(projectPath, 'remix.config.js')) || exists(join$1(projectPath, 'remix.config.ts'))) {
1738
- confidence += 10;
1739
- sources.push({ type: 'config-file', path: 'remix.config.*' });
1500
+ if (exists(join$1(projectPath, 'pages'))) {
1501
+ confidence += 5;
1502
+ sources.push({ type: 'directory', path: 'pages/' });
1740
1503
  }
1741
1504
  if (confidence === 0) {
1742
1505
  return null;
1743
1506
  }
1744
1507
  return {
1745
- id: 'remix',
1746
- name: 'Remix',
1508
+ id: 'nuxt',
1509
+ name: 'Nuxt',
1747
1510
  category: 'meta-framework',
1748
1511
  version,
1749
1512
  confidence: min(confidence, 100),
@@ -1752,39 +1515,59 @@ function remixDetector(projectPath, packageJson) {
1752
1515
  }
1753
1516
 
1754
1517
  /**
1755
- * Detect Gatsby in project.
1518
+ * Detect Qwik in project.
1756
1519
  *
1757
1520
  * @param projectPath - Project directory path
1758
1521
  * @param packageJson - Optional pre-loaded package.json
1759
1522
  * @returns Detection result or null if not detected
1523
+ *
1524
+ * @example Detecting Qwik framework
1525
+ * ```typescript
1526
+ * const result = qwikDetector('/path/to/qwik-app', {
1527
+ * dependencies: {
1528
+ * '@builder.io/qwik': '^1.0.0',
1529
+ * '@builder.io/qwik-city': '^1.0.0'
1530
+ * }
1531
+ * })
1532
+ * // => {
1533
+ * // id: 'qwik',
1534
+ * // name: 'Qwik',
1535
+ * // category: 'frontend',
1536
+ * // version: '1.0.0',
1537
+ * // confidence: 90,
1538
+ * // detectedFrom: [
1539
+ * // { type: 'package.json', field: 'dependencies.@builder.io/qwik' },
1540
+ * // { type: 'package.json', field: 'dependencies.@builder.io/qwik-city' }
1541
+ * // ]
1542
+ * // }
1543
+ * ```
1760
1544
  */
1761
- function gatsbyDetector(projectPath, packageJson) {
1545
+ function qwikDetector(projectPath, packageJson) {
1762
1546
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1763
1547
  const sources = [];
1764
1548
  let confidence = 0;
1765
1549
  let version;
1766
1550
  const deps = collectAllDependencies(pkg);
1767
- if (deps['gatsby']) {
1551
+ if (deps['@builder.io/qwik']) {
1768
1552
  confidence += 70;
1769
- version = parseVersionString(deps['gatsby']);
1770
- sources.push({ type: 'package.json', field: 'dependencies.gatsby' });
1553
+ version = parseVersionString(deps['@builder.io/qwik']);
1554
+ sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik' });
1771
1555
  }
1772
- if (exists(join$1(projectPath, 'gatsby-config.js')) || exists(join$1(projectPath, 'gatsby-config.ts'))) {
1773
- confidence += 25;
1774
- sources.push({ type: 'config-file', path: 'gatsby-config.*' });
1556
+ if (deps['@builder.io/qwik-city']) {
1557
+ confidence += 20;
1558
+ sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik-city' });
1775
1559
  }
1776
- const gatsbyPlugins = keys(deps).filter((d) => d.startsWith('gatsby-plugin-') || d.startsWith('gatsby-source-'));
1777
- if (gatsbyPlugins.length > 0) {
1778
- confidence += 5;
1779
- sources.push({ type: 'package.json', field: 'dependencies (gatsby plugins)' });
1560
+ if (exists(join$1(projectPath, 'qwik.config.ts')) || exists(join$1(projectPath, 'qwik.config.js'))) {
1561
+ confidence += 10;
1562
+ sources.push({ type: 'config-file', path: 'qwik.config.*' });
1780
1563
  }
1781
1564
  if (confidence === 0) {
1782
1565
  return null;
1783
1566
  }
1784
1567
  return {
1785
- id: 'gatsby',
1786
- name: 'Gatsby',
1787
- category: 'meta-framework',
1568
+ id: 'qwik',
1569
+ name: 'Qwik',
1570
+ category: 'frontend',
1788
1571
  version,
1789
1572
  confidence: min(confidence, 100),
1790
1573
  detectedFrom: sources,
@@ -1792,53 +1575,94 @@ function gatsbyDetector(projectPath, packageJson) {
1792
1575
  }
1793
1576
 
1794
1577
  /**
1795
- * Detect Vue in project.
1578
+ * Detect React in project.
1796
1579
  *
1797
1580
  * @param projectPath - Project directory path
1798
1581
  * @param packageJson - Optional pre-loaded package.json
1799
1582
  * @returns Detection result or null if not detected
1583
+ *
1584
+ * @example Detecting React library
1585
+ * ```typescript
1586
+ * const result = reactDetector('/path/to/react-app', {
1587
+ * dependencies: { 'react': '^18.0.0', 'react-dom': '^18.0.0' }
1588
+ * })
1589
+ * // => {
1590
+ * // id: 'react',
1591
+ * // name: 'React',
1592
+ * // category: 'frontend',
1593
+ * // version: '18.0.0',
1594
+ * // confidence: 80,
1595
+ * // detectedFrom: [
1596
+ * // { type: 'package.json', field: 'dependencies.react' },
1597
+ * // { type: 'package.json', field: 'dependencies.react-dom' }
1598
+ * // ]
1599
+ * // }
1600
+ * ```
1800
1601
  */
1801
- function vueDetector(projectPath, packageJson) {
1602
+ function reactDetector(projectPath, packageJson) {
1802
1603
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1803
1604
  const sources = [];
1804
1605
  let confidence = 0;
1805
1606
  let version;
1806
1607
  const metaFrameworks = [];
1807
1608
  const deps = collectAllDependencies(pkg);
1808
- if (deps['vue']) {
1809
- confidence += 70;
1810
- version = parseVersionString(deps['vue']);
1811
- sources.push({ type: 'package.json', field: 'dependencies.vue' });
1609
+ if (deps['react']) {
1610
+ confidence += 60;
1611
+ version = parseVersionString(deps['react']);
1612
+ sources.push({ type: 'package.json', field: 'dependencies.react' });
1812
1613
  }
1813
- if (deps['@vue/cli-service']) {
1814
- confidence += 15;
1815
- sources.push({ type: 'package.json', field: 'dependencies.@vue/cli-service' });
1614
+ if (deps['react-dom']) {
1615
+ confidence += 20;
1616
+ sources.push({ type: 'package.json', field: 'dependencies.react-dom' });
1816
1617
  }
1817
- const hasVueFiles = exists(join$1(projectPath, 'src', 'App.vue')) || exists(join$1(projectPath, 'src', 'main.vue'));
1818
- if (hasVueFiles) {
1618
+ if (deps['react-native']) {
1619
+ confidence += 20;
1620
+ sources.push({ type: 'package.json', field: 'dependencies.react-native' });
1621
+ }
1622
+ const hasJsxFiles = exists(join$1(projectPath, 'src', 'App.tsx')) ||
1623
+ exists(join$1(projectPath, 'src', 'App.jsx')) ||
1624
+ exists(join$1(projectPath, 'src', 'index.tsx')) ||
1625
+ exists(join$1(projectPath, 'src', 'index.jsx'));
1626
+ if (hasJsxFiles) {
1819
1627
  confidence += 10;
1820
- sources.push({ type: 'directory', path: 'src/*.vue' });
1628
+ sources.push({ type: 'directory', path: 'src/*.tsx or src/*.jsx' });
1821
1629
  }
1822
- if (exists(join$1(projectPath, 'vue.config.js'))) {
1823
- confidence += 5;
1824
- sources.push({ type: 'config-file', path: 'vue.config.js' });
1630
+ if (deps['next']) {
1631
+ metaFrameworks.push({
1632
+ id: 'nextjs',
1633
+ name: 'Next.js',
1634
+ category: 'meta-framework',
1635
+ version: parseVersionString(deps['next']),
1636
+ confidence: 90,
1637
+ detectedFrom: [{ type: 'package.json', field: 'dependencies.next' }],
1638
+ });
1825
1639
  }
1826
- if (deps['nuxt'] || deps['nuxt3']) {
1640
+ if (deps['gatsby']) {
1827
1641
  metaFrameworks.push({
1828
- id: 'nuxt',
1829
- name: 'Nuxt',
1642
+ id: 'gatsby',
1643
+ name: 'Gatsby',
1830
1644
  category: 'meta-framework',
1831
- version: parseVersionString(deps['nuxt'] ?? deps['nuxt3']),
1645
+ version: parseVersionString(deps['gatsby']),
1832
1646
  confidence: 90,
1833
- detectedFrom: [{ type: 'package.json', field: 'dependencies.nuxt' }],
1647
+ detectedFrom: [{ type: 'package.json', field: 'dependencies.gatsby' }],
1648
+ });
1649
+ }
1650
+ if (deps['@remix-run/react'] || deps['remix']) {
1651
+ metaFrameworks.push({
1652
+ id: 'remix',
1653
+ name: 'Remix',
1654
+ category: 'meta-framework',
1655
+ version: parseVersionString(deps['@remix-run/react'] ?? deps['remix']),
1656
+ confidence: 90,
1657
+ detectedFrom: [{ type: 'package.json', field: 'dependencies.@remix-run/react' }],
1834
1658
  });
1835
1659
  }
1836
1660
  if (confidence === 0) {
1837
1661
  return null;
1838
1662
  }
1839
1663
  return {
1840
- id: 'vue',
1841
- name: 'Vue',
1664
+ id: 'react',
1665
+ name: 'React',
1842
1666
  category: 'frontend',
1843
1667
  version,
1844
1668
  confidence: min(confidence, 100),
@@ -1848,37 +1672,58 @@ function vueDetector(projectPath, packageJson) {
1848
1672
  }
1849
1673
 
1850
1674
  /**
1851
- * Detect Nuxt in project.
1675
+ * Detect Remix in project.
1852
1676
  *
1853
1677
  * @param projectPath - Project directory path
1854
1678
  * @param packageJson - Optional pre-loaded package.json
1855
1679
  * @returns Detection result or null if not detected
1680
+ *
1681
+ * @example Detecting Remix framework
1682
+ * ```typescript
1683
+ * const result = remixDetector('/path/to/remix-app', {
1684
+ * dependencies: {
1685
+ * '@remix-run/react': '^2.0.0',
1686
+ * '@remix-run/node': '^2.0.0'
1687
+ * }
1688
+ * })
1689
+ * // => {
1690
+ * // id: 'remix',
1691
+ * // name: 'Remix',
1692
+ * // category: 'meta-framework',
1693
+ * // version: '2.0.0',
1694
+ * // confidence: 90,
1695
+ * // detectedFrom: [
1696
+ * // { type: 'package.json', field: 'dependencies.@remix-run/react' },
1697
+ * // { type: 'package.json', field: 'dependencies.@remix-run/*' }
1698
+ * // ]
1699
+ * // }
1700
+ * ```
1856
1701
  */
1857
- function nuxtDetector(projectPath, packageJson) {
1702
+ function remixDetector(projectPath, packageJson) {
1858
1703
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1859
1704
  const sources = [];
1860
1705
  let confidence = 0;
1861
1706
  let version;
1862
1707
  const deps = collectAllDependencies(pkg);
1863
- if (deps['nuxt'] || deps['nuxt3']) {
1708
+ if (deps['@remix-run/react']) {
1864
1709
  confidence += 70;
1865
- version = parseVersionString(deps['nuxt'] ?? deps['nuxt3']);
1866
- sources.push({ type: 'package.json', field: 'dependencies.nuxt' });
1710
+ version = parseVersionString(deps['@remix-run/react']);
1711
+ sources.push({ type: 'package.json', field: 'dependencies.@remix-run/react' });
1867
1712
  }
1868
- if (exists(join$1(projectPath, 'nuxt.config.js')) || exists(join$1(projectPath, 'nuxt.config.ts'))) {
1869
- confidence += 25;
1870
- sources.push({ type: 'config-file', path: 'nuxt.config.*' });
1713
+ if (deps['@remix-run/node'] || deps['@remix-run/cloudflare'] || deps['@remix-run/deno']) {
1714
+ confidence += 20;
1715
+ sources.push({ type: 'package.json', field: 'dependencies.@remix-run/*' });
1871
1716
  }
1872
- if (exists(join$1(projectPath, 'pages'))) {
1873
- confidence += 5;
1874
- sources.push({ type: 'directory', path: 'pages/' });
1717
+ if (exists(join$1(projectPath, 'remix.config.js')) || exists(join$1(projectPath, 'remix.config.ts'))) {
1718
+ confidence += 10;
1719
+ sources.push({ type: 'config-file', path: 'remix.config.*' });
1875
1720
  }
1876
1721
  if (confidence === 0) {
1877
1722
  return null;
1878
1723
  }
1879
1724
  return {
1880
- id: 'nuxt',
1881
- name: 'Nuxt',
1725
+ id: 'remix',
1726
+ name: 'Remix',
1882
1727
  category: 'meta-framework',
1883
1728
  version,
1884
1729
  confidence: min(confidence, 100),
@@ -1887,47 +1732,55 @@ function nuxtDetector(projectPath, packageJson) {
1887
1732
  }
1888
1733
 
1889
1734
  /**
1890
- * Detect Angular in project.
1735
+ * Detect Solid in project.
1891
1736
  *
1892
1737
  * @param projectPath - Project directory path
1893
1738
  * @param packageJson - Optional pre-loaded package.json
1894
1739
  * @returns Detection result or null if not detected
1740
+ *
1741
+ * @example Detecting Solid.js framework
1742
+ * ```typescript
1743
+ * const result = solidDetector('/path/to/solid-app', {
1744
+ * dependencies: { 'solid-js': '^1.8.0', 'vite-plugin-solid': '^2.0.0' }
1745
+ * })
1746
+ * // => {
1747
+ * // id: 'solid',
1748
+ * // name: 'Solid',
1749
+ * // category: 'frontend',
1750
+ * // version: '1.8.0',
1751
+ * // confidence: 90,
1752
+ * // detectedFrom: [
1753
+ * // { type: 'package.json', field: 'dependencies.solid-js' },
1754
+ * // { type: 'package.json', field: 'dependencies.vite-plugin-solid' }
1755
+ * // ]
1756
+ * // }
1757
+ * ```
1895
1758
  */
1896
- function angularDetector(projectPath, packageJson) {
1759
+ function solidDetector(projectPath, packageJson) {
1897
1760
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
1898
1761
  const sources = [];
1899
1762
  let confidence = 0;
1900
1763
  let version;
1901
1764
  const deps = collectAllDependencies(pkg);
1902
- if (deps['@angular/core']) {
1765
+ if (deps['solid-js']) {
1903
1766
  confidence += 70;
1904
- version = parseVersionString(deps['@angular/core']);
1905
- sources.push({ type: 'package.json', field: 'dependencies.@angular/core' });
1906
- }
1907
- if (deps['@angular/cli']) {
1908
- confidence += 15;
1909
- sources.push({ type: 'package.json', field: 'dependencies.@angular/cli' });
1767
+ version = parseVersionString(deps['solid-js']);
1768
+ sources.push({ type: 'package.json', field: 'dependencies.solid-js' });
1910
1769
  }
1911
- if (exists(join$1(projectPath, 'angular.json'))) {
1912
- confidence += 15;
1913
- sources.push({ type: 'config-file', path: 'angular.json' });
1770
+ if (deps['vite-plugin-solid']) {
1771
+ confidence += 20;
1772
+ sources.push({ type: 'package.json', field: 'dependencies.vite-plugin-solid' });
1914
1773
  }
1915
- if (deps['angular'] && !deps['@angular/core']) {
1916
- return {
1917
- id: 'angularjs',
1918
- name: 'AngularJS (Legacy)',
1919
- category: 'frontend',
1920
- version: parseVersionString(deps['angular']),
1921
- confidence: 80,
1922
- detectedFrom: [{ type: 'package.json', field: 'dependencies.angular' }],
1923
- };
1774
+ if (deps['solid-start'] || deps['@solidjs/start']) {
1775
+ confidence += 10;
1776
+ sources.push({ type: 'package.json', field: 'dependencies.solid-start' });
1924
1777
  }
1925
1778
  if (confidence === 0) {
1926
1779
  return null;
1927
1780
  }
1928
1781
  return {
1929
- id: 'angular',
1930
- name: 'Angular',
1782
+ id: 'solid',
1783
+ name: 'Solid',
1931
1784
  category: 'frontend',
1932
1785
  version,
1933
1786
  confidence: min(confidence, 100),
@@ -1941,6 +1794,21 @@ function angularDetector(projectPath, packageJson) {
1941
1794
  * @param projectPath - Project directory path
1942
1795
  * @param packageJson - Optional pre-loaded package.json
1943
1796
  * @returns Detection result or null if not detected
1797
+ *
1798
+ * @example Detecting Svelte framework
1799
+ * ```typescript
1800
+ * const result = svelteDetector('/path/to/svelte-app', {
1801
+ * devDependencies: { 'svelte': '^4.0.0' }
1802
+ * })
1803
+ * // => {
1804
+ * // id: 'svelte',
1805
+ * // name: 'Svelte',
1806
+ * // category: 'frontend',
1807
+ * // version: '4.0.0',
1808
+ * // confidence: 70,
1809
+ * // detectedFrom: [{ type: 'package.json', field: 'dependencies.svelte' }]
1810
+ * // }
1811
+ * ```
1944
1812
  */
1945
1813
  function svelteDetector(projectPath, packageJson) {
1946
1814
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -1993,6 +1861,21 @@ function svelteDetector(projectPath, packageJson) {
1993
1861
  * @param projectPath - Project directory path
1994
1862
  * @param packageJson - Optional pre-loaded package.json
1995
1863
  * @returns Detection result or null if not detected
1864
+ *
1865
+ * @example Detecting SvelteKit framework
1866
+ * ```typescript
1867
+ * const result = sveltekitDetector('/path/to/sveltekit-app', {
1868
+ * devDependencies: { '@sveltejs/kit': '^2.0.0', 'svelte': '^4.0.0' }
1869
+ * })
1870
+ * // => {
1871
+ * // id: 'sveltekit',
1872
+ * // name: 'SvelteKit',
1873
+ * // category: 'meta-framework',
1874
+ * // version: '2.0.0',
1875
+ * // confidence: 70,
1876
+ * // detectedFrom: [{ type: 'package.json', field: 'dependencies.@sveltejs/kit' }]
1877
+ * // }
1878
+ * ```
1996
1879
  */
1997
1880
  function sveltekitDetector(projectPath, packageJson) {
1998
1881
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2000,7 +1883,6 @@ function sveltekitDetector(projectPath, packageJson) {
2000
1883
  let confidence = 0;
2001
1884
  let version;
2002
1885
  const deps = collectAllDependencies(pkg);
2003
- // @sveltejs/kit package
2004
1886
  if (deps['@sveltejs/kit']) {
2005
1887
  confidence += 70;
2006
1888
  version = parseVersionString(deps['@sveltejs/kit']);
@@ -2028,123 +1910,76 @@ function sveltekitDetector(projectPath, packageJson) {
2028
1910
  }
2029
1911
 
2030
1912
  /**
2031
- * Detect Solid in project.
1913
+ * Detect Vue in project.
2032
1914
  *
2033
1915
  * @param projectPath - Project directory path
2034
1916
  * @param packageJson - Optional pre-loaded package.json
2035
1917
  * @returns Detection result or null if not detected
2036
- */
2037
- function solidDetector(projectPath, packageJson) {
2038
- const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2039
- const sources = [];
2040
- let confidence = 0;
2041
- let version;
2042
- const deps = collectAllDependencies(pkg);
2043
- if (deps['solid-js']) {
2044
- confidence += 70;
2045
- version = parseVersionString(deps['solid-js']);
2046
- sources.push({ type: 'package.json', field: 'dependencies.solid-js' });
2047
- }
2048
- if (deps['vite-plugin-solid']) {
2049
- confidence += 20;
2050
- sources.push({ type: 'package.json', field: 'dependencies.vite-plugin-solid' });
2051
- }
2052
- if (deps['solid-start'] || deps['@solidjs/start']) {
2053
- confidence += 10;
2054
- sources.push({ type: 'package.json', field: 'dependencies.solid-start' });
2055
- }
2056
- if (confidence === 0) {
2057
- return null;
2058
- }
2059
- return {
2060
- id: 'solid',
2061
- name: 'Solid',
2062
- category: 'frontend',
2063
- version,
2064
- confidence: min(confidence, 100),
2065
- detectedFrom: sources,
2066
- };
2067
- }
2068
-
2069
- /**
2070
- * Detect Qwik in project.
2071
1918
  *
2072
- * @param projectPath - Project directory path
2073
- * @param packageJson - Optional pre-loaded package.json
2074
- * @returns Detection result or null if not detected
1919
+ * @example Detecting Vue.js framework
1920
+ * ```typescript
1921
+ * const result = vueDetector('/path/to/vue-app', {
1922
+ * dependencies: { 'vue': '^3.0.0', '@vue/cli-service': '^5.0.0' }
1923
+ * })
1924
+ * // => {
1925
+ * // id: 'vue',
1926
+ * // name: 'Vue',
1927
+ * // category: 'frontend',
1928
+ * // version: '3.0.0',
1929
+ * // confidence: 85,
1930
+ * // detectedFrom: [
1931
+ * // { type: 'package.json', field: 'dependencies.vue' },
1932
+ * // { type: 'package.json', field: 'dependencies.@vue/cli-service' }
1933
+ * // ]
1934
+ * // }
1935
+ * ```
2075
1936
  */
2076
- function qwikDetector(projectPath, packageJson) {
1937
+ function vueDetector(projectPath, packageJson) {
2077
1938
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2078
1939
  const sources = [];
2079
1940
  let confidence = 0;
2080
1941
  let version;
1942
+ const metaFrameworks = [];
2081
1943
  const deps = collectAllDependencies(pkg);
2082
- // @builder.io/qwik package
2083
- if (deps['@builder.io/qwik']) {
1944
+ if (deps['vue']) {
2084
1945
  confidence += 70;
2085
- version = parseVersionString(deps['@builder.io/qwik']);
2086
- sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik' });
1946
+ version = parseVersionString(deps['vue']);
1947
+ sources.push({ type: 'package.json', field: 'dependencies.vue' });
2087
1948
  }
2088
- // @builder.io/qwik-city
2089
- if (deps['@builder.io/qwik-city']) {
2090
- confidence += 20;
2091
- sources.push({ type: 'package.json', field: 'dependencies.@builder.io/qwik-city' });
1949
+ if (deps['@vue/cli-service']) {
1950
+ confidence += 15;
1951
+ sources.push({ type: 'package.json', field: 'dependencies.@vue/cli-service' });
2092
1952
  }
2093
- if (exists(join$1(projectPath, 'qwik.config.ts')) || exists(join$1(projectPath, 'qwik.config.js'))) {
1953
+ const hasVueFiles = exists(join$1(projectPath, 'src', 'App.vue')) || exists(join$1(projectPath, 'src', 'main.vue'));
1954
+ if (hasVueFiles) {
2094
1955
  confidence += 10;
2095
- sources.push({ type: 'config-file', path: 'qwik.config.*' });
2096
- }
2097
- if (confidence === 0) {
2098
- return null;
2099
- }
2100
- return {
2101
- id: 'qwik',
2102
- name: 'Qwik',
2103
- category: 'frontend',
2104
- version,
2105
- confidence: min(confidence, 100),
2106
- detectedFrom: sources,
2107
- };
2108
- }
2109
-
2110
- /**
2111
- * Detect Astro in project.
2112
- *
2113
- * @param projectPath - Project directory path
2114
- * @param packageJson - Optional pre-loaded package.json
2115
- * @returns Detection result or null if not detected
2116
- */
2117
- function astroDetector(projectPath, packageJson) {
2118
- const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2119
- const sources = [];
2120
- let confidence = 0;
2121
- let version;
2122
- const deps = collectAllDependencies(pkg);
2123
- if (deps['astro']) {
2124
- confidence += 70;
2125
- version = parseVersionString(deps['astro']);
2126
- sources.push({ type: 'package.json', field: 'dependencies.astro' });
2127
- }
2128
- if (exists(join$1(projectPath, 'astro.config.mjs')) ||
2129
- exists(join$1(projectPath, 'astro.config.ts')) ||
2130
- exists(join$1(projectPath, 'astro.config.js'))) {
2131
- confidence += 25;
2132
- sources.push({ type: 'config-file', path: 'astro.config.*' });
1956
+ sources.push({ type: 'directory', path: 'src/*.vue' });
2133
1957
  }
2134
- if (exists(join$1(projectPath, 'src', 'pages'))) {
1958
+ if (exists(join$1(projectPath, 'vue.config.js'))) {
2135
1959
  confidence += 5;
2136
- sources.push({ type: 'directory', path: 'src/pages/' });
1960
+ sources.push({ type: 'config-file', path: 'vue.config.js' });
1961
+ }
1962
+ if (deps['nuxt'] || deps['nuxt3']) {
1963
+ metaFrameworks.push({
1964
+ id: 'nuxt',
1965
+ name: 'Nuxt',
1966
+ category: 'meta-framework',
1967
+ version: parseVersionString(deps['nuxt'] ?? deps['nuxt3']),
1968
+ confidence: 90,
1969
+ detectedFrom: [{ type: 'package.json', field: 'dependencies.nuxt' }],
1970
+ });
2137
1971
  }
2138
1972
  if (confidence === 0) {
2139
1973
  return null;
2140
1974
  }
2141
1975
  return {
2142
- id: 'astro',
2143
- name: 'Astro',
2144
- category: 'meta-framework',
1976
+ id: 'vue',
1977
+ name: 'Vue',
1978
+ category: 'frontend',
2145
1979
  version,
2146
1980
  confidence: min(confidence, 100),
2147
1981
  detectedFrom: sources,
1982
+ metaFrameworks: metaFrameworks.length > 0 ? metaFrameworks : undefined,
2148
1983
  };
2149
1984
  }
2150
1985
 
@@ -2169,6 +2004,17 @@ const frameworkDetectors = [
2169
2004
  * @param projectPath - Project directory path
2170
2005
  * @param packageJson - Optional pre-loaded package.json
2171
2006
  * @returns Array of detected frameworks, sorted by confidence
2007
+ *
2008
+ * @example Detecting multiple frontend frameworks
2009
+ * ```typescript
2010
+ * const frameworks = detectFrontendFrameworks('/path/to/nextjs-app', {
2011
+ * dependencies: { 'react': '^18.0.0', 'next': '^14.0.0' }
2012
+ * })
2013
+ * // => [
2014
+ * // { id: 'nextjs', name: 'Next.js', category: 'meta-framework', confidence: 70, ... },
2015
+ * // { id: 'react', name: 'React', category: 'frontend', confidence: 60, ... }
2016
+ * // ]
2017
+ * ```
2172
2018
  */
2173
2019
  function detectFrontendFrameworks(projectPath, packageJson) {
2174
2020
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2189,6 +2035,14 @@ function detectFrontendFrameworks(projectPath, packageJson) {
2189
2035
  * @param projectPath - Project directory path
2190
2036
  * @param packageJson - Optional pre-loaded package.json
2191
2037
  * @returns Detection result or null if not detected
2038
+ *
2039
+ * @example Detecting AngularJS framework
2040
+ * ```typescript
2041
+ * const result = angularJSDetector('/path/to/project', {
2042
+ * dependencies: { angular: '^1.8.0', 'angular-route': '^1.8.0' },
2043
+ * })
2044
+ * // => { id: 'angularjs', name: 'AngularJS', confidence: 85, version: '1.8.0', ... }
2045
+ * ```
2192
2046
  */
2193
2047
  function angularJSDetector(projectPath, packageJson) {
2194
2048
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2196,23 +2050,19 @@ function angularJSDetector(projectPath, packageJson) {
2196
2050
  let confidence = 0;
2197
2051
  let version;
2198
2052
  const deps = collectAllDependencies(pkg);
2199
- // AngularJS package (angular, not @angular/core)
2200
2053
  if (deps['angular']) {
2201
2054
  confidence += 70;
2202
2055
  version = parseVersionString(deps['angular']);
2203
2056
  sources.push({ type: 'package.json', field: 'dependencies.angular' });
2204
2057
  }
2205
- // AngularJS router
2206
2058
  if (deps['angular-route']) {
2207
2059
  confidence += 15;
2208
2060
  sources.push({ type: 'package.json', field: 'dependencies.angular-route' });
2209
2061
  }
2210
- // AngularJS resource
2211
2062
  if (deps['angular-resource']) {
2212
2063
  confidence += 10;
2213
2064
  sources.push({ type: 'package.json', field: 'dependencies.angular-resource' });
2214
2065
  }
2215
- // AngularJS animate
2216
2066
  if (deps['angular-animate']) {
2217
2067
  confidence += 5;
2218
2068
  sources.push({ type: 'package.json', field: 'dependencies.angular-animate' });
@@ -2236,6 +2086,14 @@ function angularJSDetector(projectPath, packageJson) {
2236
2086
  * @param projectPath - Project directory path
2237
2087
  * @param packageJson - Optional pre-loaded package.json
2238
2088
  * @returns Detection result or null if not detected
2089
+ *
2090
+ * @example Detecting Backbone.js framework
2091
+ * ```typescript
2092
+ * const result = backboneDetector('/path/to/project', {
2093
+ * dependencies: { backbone: '^1.4.0', underscore: '^1.13.0' },
2094
+ * })
2095
+ * // => { id: 'backbone', name: 'Backbone.js', confidence: 85, version: '1.4.0', ... }
2096
+ * ```
2239
2097
  */
2240
2098
  function backboneDetector(projectPath, packageJson) {
2241
2099
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2243,23 +2101,19 @@ function backboneDetector(projectPath, packageJson) {
2243
2101
  let confidence = 0;
2244
2102
  let version;
2245
2103
  const deps = collectAllDependencies(pkg);
2246
- // Backbone package
2247
2104
  if (deps['backbone']) {
2248
2105
  confidence += 70;
2249
2106
  version = parseVersionString(deps['backbone']);
2250
2107
  sources.push({ type: 'package.json', field: 'dependencies.backbone' });
2251
- // Underscore (commonly used with Backbone)
2252
2108
  if (deps['underscore']) {
2253
2109
  confidence += 15;
2254
2110
  sources.push({ type: 'package.json', field: 'dependencies.underscore' });
2255
2111
  }
2256
- // Lodash can be used as underscore replacement
2257
2112
  if (deps['lodash']) {
2258
2113
  confidence += 5;
2259
2114
  sources.push({ type: 'package.json', field: 'dependencies.lodash' });
2260
2115
  }
2261
2116
  }
2262
- // Marionette (Backbone framework)
2263
2117
  if (deps['backbone.marionette'] || deps['marionette']) {
2264
2118
  confidence += 10;
2265
2119
  sources.push({ type: 'package.json', field: 'dependencies.backbone.marionette' });
@@ -2283,6 +2137,15 @@ function backboneDetector(projectPath, packageJson) {
2283
2137
  * @param projectPath - Project directory path
2284
2138
  * @param packageJson - Optional pre-loaded package.json
2285
2139
  * @returns Detection result or null if not detected
2140
+ *
2141
+ * @example Detecting Ember.js framework
2142
+ * ```typescript
2143
+ * const result = emberDetector('/path/to/project', {
2144
+ * dependencies: { 'ember-source': '^4.0.0' },
2145
+ * devDependencies: { 'ember-cli': '^4.0.0' },
2146
+ * })
2147
+ * // => { id: 'ember', name: 'Ember.js', confidence: 90, version: '4.0.0', ... }
2148
+ * ```
2286
2149
  */
2287
2150
  function emberDetector(projectPath, packageJson) {
2288
2151
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2290,18 +2153,15 @@ function emberDetector(projectPath, packageJson) {
2290
2153
  let confidence = 0;
2291
2154
  let version;
2292
2155
  const deps = collectAllDependencies(pkg);
2293
- // Ember source package
2294
2156
  if (deps['ember-source']) {
2295
2157
  confidence += 70;
2296
2158
  version = parseVersionString(deps['ember-source']);
2297
2159
  sources.push({ type: 'package.json', field: 'dependencies.ember-source' });
2298
2160
  }
2299
- // Ember CLI
2300
2161
  if (deps['ember-cli']) {
2301
2162
  confidence += 20;
2302
2163
  sources.push({ type: 'package.json', field: 'devDependencies.ember-cli' });
2303
2164
  }
2304
- // Ember Data
2305
2165
  if (deps['ember-data']) {
2306
2166
  confidence += 10;
2307
2167
  sources.push({ type: 'package.json', field: 'dependencies.ember-data' });
@@ -2325,6 +2185,14 @@ function emberDetector(projectPath, packageJson) {
2325
2185
  * @param projectPath - Project directory path
2326
2186
  * @param packageJson - Optional pre-loaded package.json
2327
2187
  * @returns Detection result or null if not detected
2188
+ *
2189
+ * @example Detecting jQuery library
2190
+ * ```typescript
2191
+ * const result = jqueryDetector('/path/to/project', {
2192
+ * dependencies: { jquery: '^3.6.0', 'jquery-ui': '^1.13.0' },
2193
+ * })
2194
+ * // => { id: 'jquery', name: 'jQuery', confidence: 90, version: '3.6.0', ... }
2195
+ * ```
2328
2196
  */
2329
2197
  function jqueryDetector(projectPath, packageJson) {
2330
2198
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2332,18 +2200,15 @@ function jqueryDetector(projectPath, packageJson) {
2332
2200
  let confidence = 0;
2333
2201
  let version;
2334
2202
  const deps = collectAllDependencies(pkg);
2335
- // jQuery package
2336
2203
  if (deps['jquery']) {
2337
2204
  confidence += 80;
2338
2205
  version = parseVersionString(deps['jquery']);
2339
2206
  sources.push({ type: 'package.json', field: 'dependencies.jquery' });
2340
2207
  }
2341
- // jQuery UI
2342
2208
  if (deps['jquery-ui']) {
2343
2209
  confidence += 10;
2344
2210
  sources.push({ type: 'package.json', field: 'dependencies.jquery-ui' });
2345
2211
  }
2346
- // jQuery plugins
2347
2212
  if (deps['jquery-validation']) {
2348
2213
  confidence += 5;
2349
2214
  sources.push({ type: 'package.json', field: 'dependencies.jquery-validation' });
@@ -2361,32 +2226,90 @@ function jqueryDetector(projectPath, packageJson) {
2361
2226
  };
2362
2227
  }
2363
2228
 
2364
- /** All legacy framework detectors */
2365
- const legacyDetectors = [
2366
- { id: 'angularjs', name: 'AngularJS', category: 'legacy-frontend', detect: angularJSDetector },
2367
- { id: 'backbone', name: 'Backbone.js', category: 'legacy-frontend', detect: backboneDetector },
2368
- { id: 'ember', name: 'Ember.js', category: 'legacy-frontend', detect: emberDetector },
2369
- { id: 'jquery', name: 'jQuery', category: 'legacy-frontend', detect: jqueryDetector },
2370
- ];
2371
- /**
2372
- * Detect all legacy frameworks in project.
2373
- *
2374
- * @param projectPath - Project directory path
2375
- * @param packageJson - Optional pre-loaded package.json
2376
- * @returns Array of detected legacy frameworks, sorted by confidence
2377
- */
2378
- function detectLegacyFrameworks(projectPath, packageJson) {
2379
- const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2380
- const results = [];
2381
- for (const detector of legacyDetectors) {
2382
- const detection = detector.detect(projectPath, pkg ?? undefined);
2383
- if (detection) {
2384
- results.push(detection);
2385
- }
2386
- }
2387
- return results.sort((a, b) => b.confidence - a.confidence);
2388
- }
2389
-
2229
+ /** All legacy framework detectors */
2230
+ const legacyDetectors = [
2231
+ { id: 'angularjs', name: 'AngularJS', category: 'legacy-frontend', detect: angularJSDetector },
2232
+ { id: 'backbone', name: 'Backbone.js', category: 'legacy-frontend', detect: backboneDetector },
2233
+ { id: 'ember', name: 'Ember.js', category: 'legacy-frontend', detect: emberDetector },
2234
+ { id: 'jquery', name: 'jQuery', category: 'legacy-frontend', detect: jqueryDetector },
2235
+ ];
2236
+ /**
2237
+ * Detect all legacy frameworks in project.
2238
+ *
2239
+ * @param projectPath - Project directory path
2240
+ * @param packageJson - Optional pre-loaded package.json
2241
+ * @returns Array of detected legacy frameworks, sorted by confidence
2242
+ *
2243
+ * @example Detecting legacy frameworks
2244
+ * ```typescript
2245
+ * const results = detectLegacyFrameworks('/path/to/project', {
2246
+ * dependencies: { jquery: '^3.6.0', backbone: '^1.4.0' },
2247
+ * })
2248
+ * // => [{ id: 'jquery', confidence: 80 }, { id: 'backbone', confidence: 70 }]
2249
+ * ```
2250
+ */
2251
+ function detectLegacyFrameworks(projectPath, packageJson) {
2252
+ const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2253
+ const results = [];
2254
+ for (const detector of legacyDetectors) {
2255
+ const detection = detector.detect(projectPath, pkg ?? undefined);
2256
+ if (detection) {
2257
+ results.push(detection);
2258
+ }
2259
+ }
2260
+ return results.sort((a, b) => b.confidence - a.confidence);
2261
+ }
2262
+
2263
+ /**
2264
+ * Detect Biome in project.
2265
+ *
2266
+ * @param projectPath - Project directory path
2267
+ * @param packageJson - Optional pre-loaded package.json
2268
+ * @returns Detection result or null if not detected
2269
+ *
2270
+ * @example Detecting Biome linter
2271
+ * ```typescript
2272
+ * const result = biomeDetector('/path/to/project', {
2273
+ * devDependencies: { '@biomejs/biome': '^1.5.0' },
2274
+ * })
2275
+ * // => { id: 'biome', name: 'Biome', confidence: 70, version: '1.5.0', ... }
2276
+ * ```
2277
+ */
2278
+ function biomeDetector(projectPath, packageJson) {
2279
+ const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2280
+ const sources = [];
2281
+ let confidence = 0;
2282
+ let configPath;
2283
+ let version;
2284
+ const deps = collectAllDependencies(pkg);
2285
+ if (deps['@biomejs/biome']) {
2286
+ confidence += 70;
2287
+ version = parseVersionString(deps['@biomejs/biome']);
2288
+ sources.push({ type: 'package.json', field: 'dependencies.@biomejs/biome' });
2289
+ }
2290
+ if (exists(join$1(projectPath, 'biome.json'))) {
2291
+ confidence += 30;
2292
+ configPath = 'biome.json';
2293
+ sources.push({ type: 'config-file', path: 'biome.json' });
2294
+ }
2295
+ if (!configPath && exists(join$1(projectPath, 'biome.jsonc'))) {
2296
+ confidence += 30;
2297
+ configPath = 'biome.jsonc';
2298
+ sources.push({ type: 'config-file', path: 'biome.jsonc' });
2299
+ }
2300
+ if (confidence === 0) {
2301
+ return null;
2302
+ }
2303
+ return {
2304
+ id: 'biome',
2305
+ name: 'Biome',
2306
+ version,
2307
+ configPath,
2308
+ confidence: min(confidence, 100),
2309
+ detectedFrom: sources,
2310
+ };
2311
+ }
2312
+
2390
2313
  /** Config patterns for ESLint */
2391
2314
  const ESLINT_CONFIG_PATTERNS = [
2392
2315
  'eslint.config.js',
@@ -2406,6 +2329,15 @@ const ESLINT_CONFIG_PATTERNS = [
2406
2329
  * @param projectPath - Project directory path
2407
2330
  * @param packageJson - Optional pre-loaded package.json
2408
2331
  * @returns Detection result or null if not detected
2332
+ *
2333
+ * @example Detecting ESLint linter
2334
+ * ```typescript
2335
+ * const result = eslintDetector('/path/to/project', {
2336
+ * devDependencies: { eslint: '^8.50.0', '@typescript-eslint/parser': '^6.0.0' },
2337
+ * scripts: { lint: 'eslint src/' },
2338
+ * })
2339
+ * // => { id: 'eslint', name: 'ESLint', confidence: 65, version: '8.50.0', ... }
2340
+ * ```
2409
2341
  */
2410
2342
  function eslintDetector(projectPath, packageJson) {
2411
2343
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2469,6 +2401,15 @@ const PRETTIER_CONFIG_PATTERNS = [
2469
2401
  * @param projectPath - Project directory path
2470
2402
  * @param packageJson - Optional pre-loaded package.json
2471
2403
  * @returns Detection result or null if not detected
2404
+ *
2405
+ * @example Detecting Prettier formatter
2406
+ * ```typescript
2407
+ * const result = prettierDetector('/path/to/project', {
2408
+ * devDependencies: { prettier: '^3.0.0' },
2409
+ * scripts: { format: 'prettier --write .' },
2410
+ * })
2411
+ * // => { id: 'prettier', name: 'Prettier', confidence: 55, version: '3.0.0', ... }
2412
+ * ```
2472
2413
  */
2473
2414
  function prettierDetector(projectPath, packageJson) {
2474
2415
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2490,7 +2431,6 @@ function prettierDetector(projectPath, packageJson) {
2490
2431
  confidence += 30;
2491
2432
  sources.push({ type: 'package.json', field: 'prettier' });
2492
2433
  }
2493
- // .prettierignore file
2494
2434
  if (exists(join$1(projectPath, '.prettierignore'))) {
2495
2435
  confidence += 10;
2496
2436
  sources.push({ type: 'config-file', path: '.prettierignore' });
@@ -2532,6 +2472,14 @@ const STYLELINT_CONFIG_PATTERNS = [
2532
2472
  * @param projectPath - Project directory path
2533
2473
  * @param packageJson - Optional pre-loaded package.json
2534
2474
  * @returns Detection result or null if not detected
2475
+ *
2476
+ * @example Detecting Stylelint linter
2477
+ * ```typescript
2478
+ * const result = stylelintDetector('/path/to/project', {
2479
+ * devDependencies: { stylelint: '^15.0.0', 'stylelint-config-standard': '^30.0.0' },
2480
+ * })
2481
+ * // => { id: 'stylelint', name: 'Stylelint', confidence: 65, version: '15.0.0', ... }
2482
+ * ```
2535
2483
  */
2536
2484
  function stylelintDetector(projectPath, packageJson) {
2537
2485
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2571,49 +2519,6 @@ function stylelintDetector(projectPath, packageJson) {
2571
2519
  };
2572
2520
  }
2573
2521
 
2574
- /**
2575
- * Detect Biome in project.
2576
- *
2577
- * @param projectPath - Project directory path
2578
- * @param packageJson - Optional pre-loaded package.json
2579
- * @returns Detection result or null if not detected
2580
- */
2581
- function biomeDetector(projectPath, packageJson) {
2582
- const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2583
- const sources = [];
2584
- let confidence = 0;
2585
- let configPath;
2586
- let version;
2587
- const deps = collectAllDependencies(pkg);
2588
- // @biomejs/biome package
2589
- if (deps['@biomejs/biome']) {
2590
- confidence += 70;
2591
- version = parseVersionString(deps['@biomejs/biome']);
2592
- sources.push({ type: 'package.json', field: 'dependencies.@biomejs/biome' });
2593
- }
2594
- if (exists(join$1(projectPath, 'biome.json'))) {
2595
- confidence += 30;
2596
- configPath = 'biome.json';
2597
- sources.push({ type: 'config-file', path: 'biome.json' });
2598
- }
2599
- if (!configPath && exists(join$1(projectPath, 'biome.jsonc'))) {
2600
- confidence += 30;
2601
- configPath = 'biome.jsonc';
2602
- sources.push({ type: 'config-file', path: 'biome.jsonc' });
2603
- }
2604
- if (confidence === 0) {
2605
- return null;
2606
- }
2607
- return {
2608
- id: 'biome',
2609
- name: 'Biome',
2610
- version,
2611
- configPath,
2612
- confidence: min(confidence, 100),
2613
- detectedFrom: sources,
2614
- };
2615
- }
2616
-
2617
2522
  /** All linting tool detectors */
2618
2523
  const lintingDetectors = [
2619
2524
  { id: 'eslint', name: 'ESLint', detect: eslintDetector },
@@ -2627,6 +2532,14 @@ const lintingDetectors = [
2627
2532
  * @param projectPath - Project directory path
2628
2533
  * @param packageJson - Optional pre-loaded package.json
2629
2534
  * @returns Array of detected linting tools, sorted by confidence
2535
+ *
2536
+ * @example Detecting multiple linting tools
2537
+ * ```typescript
2538
+ * const results = detectLintingTools('/path/to/project', {
2539
+ * devDependencies: { eslint: '^8.0.0', prettier: '^3.0.0' },
2540
+ * })
2541
+ * // => [{ id: 'eslint', confidence: 50 }, { id: 'prettier', confidence: 50 }]
2542
+ * ```
2630
2543
  */
2631
2544
  function detectLintingTools(projectPath, packageJson) {
2632
2545
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -2641,140 +2554,179 @@ function detectLintingTools(projectPath, packageJson) {
2641
2554
  }
2642
2555
 
2643
2556
  /**
2644
- * Detect NX in project.
2557
+ * Detect Lerna in project.
2645
2558
  *
2646
2559
  * @param workspacePath - Workspace directory path
2647
2560
  * @param packageJson - Optional pre-loaded package.json
2648
2561
  * @returns Detection result or null if not detected
2562
+ *
2563
+ * @example Detecting Lerna monorepo
2564
+ * ```typescript
2565
+ * // Project with lerna.json config file
2566
+ * const result = lernaDetector('/path/to/lerna-project')
2567
+ * // => {
2568
+ * // id: 'lerna',
2569
+ * // name: 'Lerna',
2570
+ * // confidence: 80,
2571
+ * // configPath: 'lerna.json',
2572
+ * // detectedFrom: [{ type: 'config-file', path: 'lerna.json' }]
2573
+ * // }
2574
+ * ```
2649
2575
  */
2650
- function nxDetector(workspacePath, packageJson) {
2576
+ function lernaDetector(workspacePath, packageJson) {
2651
2577
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
2652
2578
  const sources = [];
2653
2579
  let confidence = 0;
2654
2580
  let version;
2655
- let workspaceLayout;
2656
- const nxJsonPath = join$1(workspacePath, 'nx.json');
2657
- if (exists(nxJsonPath)) {
2658
- confidence += 70;
2659
- sources.push({ type: 'config-file', path: 'nx.json' });
2581
+ let configPath;
2582
+ const lernaJsonPath = join$1(workspacePath, 'lerna.json');
2583
+ if (exists(lernaJsonPath)) {
2584
+ confidence += 80;
2585
+ configPath = 'lerna.json';
2586
+ sources.push({ type: 'config-file', path: 'lerna.json' });
2660
2587
  }
2661
2588
  const deps = collectAllDependencies(pkg);
2662
- if (deps['nx']) {
2663
- confidence += 20;
2664
- version = parseVersionString(deps['nx']);
2665
- sources.push({ type: 'package.json', field: 'dependencies.nx' });
2666
- }
2667
- const hasApps = exists(join$1(workspacePath, 'apps'));
2668
- const hasLibs = exists(join$1(workspacePath, 'libs'));
2669
- if (hasApps || hasLibs) {
2670
- confidence += 10;
2671
- sources.push({ type: 'directory', path: 'apps/ or libs/' });
2672
- workspaceLayout = {
2673
- appsDir: hasApps ? 'apps' : '',
2674
- libsDir: hasLibs ? 'libs' : '',
2675
- };
2589
+ if (deps['lerna']) {
2590
+ confidence += 15;
2591
+ version = parseVersionString(deps['lerna']);
2592
+ sources.push({ type: 'package.json', field: 'dependencies.lerna' });
2676
2593
  }
2677
- const nxPackages = keys(deps).filter((d) => d.startsWith('@nx/') || d.startsWith('@nrwl/'));
2678
- if (nxPackages.length > 0) {
2679
- confidence += 10;
2680
- sources.push({ type: 'package.json', field: '@nx/* packages' });
2594
+ if (exists(join$1(workspacePath, 'packages'))) {
2595
+ confidence += 5;
2596
+ sources.push({ type: 'directory', path: 'packages/' });
2681
2597
  }
2682
2598
  if (confidence === 0) {
2683
2599
  return null;
2684
2600
  }
2685
2601
  return {
2686
- id: 'nx',
2687
- name: 'NX',
2602
+ id: 'lerna',
2603
+ name: 'Lerna',
2688
2604
  version,
2689
- configPath: exists(nxJsonPath) ? 'nx.json' : undefined,
2605
+ configPath,
2690
2606
  confidence: min(confidence, 100),
2691
2607
  detectedFrom: sources,
2692
- workspaceLayout,
2693
2608
  };
2694
2609
  }
2695
2610
 
2696
2611
  /**
2697
- * Detect Turborepo in project.
2612
+ * Detect npm workspaces in project.
2698
2613
  *
2699
2614
  * @param workspacePath - Workspace directory path
2700
2615
  * @param packageJson - Optional pre-loaded package.json
2701
2616
  * @returns Detection result or null if not detected
2617
+ *
2618
+ * @example Detecting npm workspaces
2619
+ * ```typescript
2620
+ * // Project with workspaces in package.json and package-lock.json
2621
+ * const result = npmWorkspacesDetector('/path/to/npm-project')
2622
+ * // => {
2623
+ * // id: 'npm-workspaces',
2624
+ * // name: 'npm Workspaces',
2625
+ * // confidence: 90,
2626
+ * // configPath: 'package.json',
2627
+ * // detectedFrom: [
2628
+ * // { type: 'package.json', field: 'workspaces' },
2629
+ * // { type: 'lockfile', path: 'package-lock.json' }
2630
+ * // ]
2631
+ * // }
2632
+ * ```
2702
2633
  */
2703
- function turborepoDetector(workspacePath, packageJson) {
2634
+ function npmWorkspacesDetector(workspacePath, packageJson) {
2704
2635
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
2705
2636
  const sources = [];
2706
2637
  let confidence = 0;
2707
- let version;
2708
- let configPath;
2709
- const turboJsonPath = join$1(workspacePath, 'turbo.json');
2710
- if (exists(turboJsonPath)) {
2638
+ if (pkg?.workspaces) {
2711
2639
  confidence += 80;
2712
- configPath = 'turbo.json';
2713
- sources.push({ type: 'config-file', path: 'turbo.json' });
2640
+ sources.push({ type: 'package.json', field: 'workspaces' });
2714
2641
  }
2715
- const deps = collectAllDependencies(pkg);
2716
- if (deps['turbo']) {
2717
- confidence += 15;
2718
- version = parseVersionString(deps['turbo']);
2719
- sources.push({ type: 'package.json', field: 'dependencies.turbo' });
2642
+ if (exists(join$1(workspacePath, 'package-lock.json'))) {
2643
+ confidence += 10;
2644
+ sources.push({ type: 'lockfile', path: 'package-lock.json' });
2720
2645
  }
2721
- const scripts = pkg?.scripts ?? {};
2722
- if (values(scripts).some((s) => s?.includes('turbo'))) {
2723
- confidence += 5;
2724
- sources.push({ type: 'package.json', field: 'scripts (turbo commands)' });
2646
+ if (exists(join$1(workspacePath, 'yarn.lock'))) {
2647
+ return null;
2725
2648
  }
2726
2649
  if (confidence === 0) {
2727
2650
  return null;
2728
2651
  }
2729
2652
  return {
2730
- id: 'turborepo',
2731
- name: 'Turborepo',
2732
- version,
2733
- configPath,
2653
+ id: 'npm-workspaces',
2654
+ name: 'npm Workspaces',
2655
+ configPath: 'package.json',
2734
2656
  confidence: min(confidence, 100),
2735
2657
  detectedFrom: sources,
2736
2658
  };
2737
2659
  }
2738
2660
 
2739
2661
  /**
2740
- * Detect Lerna in project.
2662
+ * Detect NX in project.
2741
2663
  *
2742
2664
  * @param workspacePath - Workspace directory path
2743
2665
  * @param packageJson - Optional pre-loaded package.json
2744
2666
  * @returns Detection result or null if not detected
2667
+ *
2668
+ * @example Detecting NX workspace
2669
+ * ```typescript
2670
+ * // Project with nx.json and apps/libs directories
2671
+ * const result = nxDetector('/path/to/nx-workspace')
2672
+ * // => {
2673
+ * // id: 'nx',
2674
+ * // name: 'NX',
2675
+ * // confidence: 100,
2676
+ * // configPath: 'nx.json',
2677
+ * // version: '17.0.0',
2678
+ * // workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
2679
+ * // detectedFrom: [
2680
+ * // { type: 'config-file', path: 'nx.json' },
2681
+ * // { type: 'package.json', field: 'dependencies.nx' },
2682
+ * // { type: 'directory', path: 'apps/ or libs/' }
2683
+ * // ]
2684
+ * // }
2685
+ * ```
2745
2686
  */
2746
- function lernaDetector(workspacePath, packageJson) {
2687
+ function nxDetector(workspacePath, packageJson) {
2747
2688
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
2748
2689
  const sources = [];
2749
2690
  let confidence = 0;
2750
2691
  let version;
2751
- let configPath;
2752
- const lernaJsonPath = join$1(workspacePath, 'lerna.json');
2753
- if (exists(lernaJsonPath)) {
2754
- confidence += 80;
2755
- configPath = 'lerna.json';
2756
- sources.push({ type: 'config-file', path: 'lerna.json' });
2692
+ let workspaceLayout;
2693
+ const nxJsonPath = join$1(workspacePath, 'nx.json');
2694
+ if (exists(nxJsonPath)) {
2695
+ confidence += 70;
2696
+ sources.push({ type: 'config-file', path: 'nx.json' });
2757
2697
  }
2758
2698
  const deps = collectAllDependencies(pkg);
2759
- if (deps['lerna']) {
2760
- confidence += 15;
2761
- version = parseVersionString(deps['lerna']);
2762
- sources.push({ type: 'package.json', field: 'dependencies.lerna' });
2699
+ if (deps['nx']) {
2700
+ confidence += 20;
2701
+ version = parseVersionString(deps['nx']);
2702
+ sources.push({ type: 'package.json', field: 'dependencies.nx' });
2763
2703
  }
2764
- if (exists(join$1(workspacePath, 'packages'))) {
2765
- confidence += 5;
2766
- sources.push({ type: 'directory', path: 'packages/' });
2704
+ const hasApps = exists(join$1(workspacePath, 'apps'));
2705
+ const hasLibs = exists(join$1(workspacePath, 'libs'));
2706
+ if (hasApps || hasLibs) {
2707
+ confidence += 10;
2708
+ sources.push({ type: 'directory', path: 'apps/ or libs/' });
2709
+ workspaceLayout = {
2710
+ appsDir: hasApps ? 'apps' : '',
2711
+ libsDir: hasLibs ? 'libs' : '',
2712
+ };
2713
+ }
2714
+ const nxPackages = keys(deps).filter((d) => d.startsWith('@nx/') || d.startsWith('@nrwl/'));
2715
+ if (nxPackages.length > 0) {
2716
+ confidence += 10;
2717
+ sources.push({ type: 'package.json', field: '@nx/* packages' });
2767
2718
  }
2768
2719
  if (confidence === 0) {
2769
2720
  return null;
2770
2721
  }
2771
2722
  return {
2772
- id: 'lerna',
2773
- name: 'Lerna',
2723
+ id: 'nx',
2724
+ name: 'NX',
2774
2725
  version,
2775
- configPath,
2726
+ configPath: exists(nxJsonPath) ? 'nx.json' : undefined,
2776
2727
  confidence: min(confidence, 100),
2777
2728
  detectedFrom: sources,
2729
+ workspaceLayout,
2778
2730
  };
2779
2731
  }
2780
2732
 
@@ -2784,6 +2736,19 @@ function lernaDetector(workspacePath, packageJson) {
2784
2736
  * @param workspacePath - Workspace directory path
2785
2737
  * @param packageJson - Optional pre-loaded package.json
2786
2738
  * @returns Detection result or null if not detected
2739
+ *
2740
+ * @example Detecting Rush monorepo
2741
+ * ```typescript
2742
+ * // Project with rush.json config file
2743
+ * const result = rushDetector('/path/to/rush-project')
2744
+ * // => {
2745
+ * // id: 'rush',
2746
+ * // name: 'Rush',
2747
+ * // confidence: 90,
2748
+ * // configPath: 'rush.json',
2749
+ * // detectedFrom: [{ type: 'config-file', path: 'rush.json' }]
2750
+ * // }
2751
+ * ```
2787
2752
  */
2788
2753
  function rushDetector(workspacePath, packageJson) {
2789
2754
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
@@ -2821,66 +2786,60 @@ function rushDetector(workspacePath, packageJson) {
2821
2786
  }
2822
2787
 
2823
2788
  /**
2824
- * Detect pnpm workspaces in project.
2825
- *
2826
- * @param workspacePath - Workspace directory path
2827
- * @returns Detection result or null if not detected
2828
- */
2829
- function pnpmWorkspacesDetector(workspacePath) {
2830
- const sources = [];
2831
- let confidence = 0;
2832
- let configPath;
2833
- const pnpmWorkspacePath = join$1(workspacePath, 'pnpm-workspace.yaml');
2834
- if (exists(pnpmWorkspacePath)) {
2835
- confidence += 90;
2836
- configPath = 'pnpm-workspace.yaml';
2837
- sources.push({ type: 'config-file', path: 'pnpm-workspace.yaml' });
2838
- }
2839
- if (exists(join$1(workspacePath, 'pnpm-lock.yaml'))) {
2840
- confidence += 10;
2841
- sources.push({ type: 'lockfile', path: 'pnpm-lock.yaml' });
2842
- }
2843
- if (confidence === 0) {
2844
- return null;
2845
- }
2846
- return {
2847
- id: 'pnpm-workspaces',
2848
- name: 'pnpm Workspaces',
2849
- configPath,
2850
- confidence: min(confidence, 100),
2851
- detectedFrom: sources,
2852
- };
2853
- }
2854
-
2855
- /**
2856
- * Detect npm workspaces in project.
2789
+ * Detect Turborepo in project.
2857
2790
  *
2858
2791
  * @param workspacePath - Workspace directory path
2859
2792
  * @param packageJson - Optional pre-loaded package.json
2860
2793
  * @returns Detection result or null if not detected
2794
+ *
2795
+ * @example Detecting Turborepo monorepo
2796
+ * ```typescript
2797
+ * // Project with turbo.json and turbo dependency
2798
+ * const result = turborepoDetector('/path/to/turbo-project')
2799
+ * // => {
2800
+ * // id: 'turborepo',
2801
+ * // name: 'Turborepo',
2802
+ * // confidence: 95,
2803
+ * // configPath: 'turbo.json',
2804
+ * // version: '2.0.0',
2805
+ * // detectedFrom: [
2806
+ * // { type: 'config-file', path: 'turbo.json' },
2807
+ * // { type: 'package.json', field: 'dependencies.turbo' }
2808
+ * // ]
2809
+ * // }
2810
+ * ```
2861
2811
  */
2862
- function npmWorkspacesDetector(workspacePath, packageJson) {
2812
+ function turborepoDetector(workspacePath, packageJson) {
2863
2813
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
2864
2814
  const sources = [];
2865
2815
  let confidence = 0;
2866
- if (pkg?.workspaces) {
2816
+ let version;
2817
+ let configPath;
2818
+ const turboJsonPath = join$1(workspacePath, 'turbo.json');
2819
+ if (exists(turboJsonPath)) {
2867
2820
  confidence += 80;
2868
- sources.push({ type: 'package.json', field: 'workspaces' });
2821
+ configPath = 'turbo.json';
2822
+ sources.push({ type: 'config-file', path: 'turbo.json' });
2869
2823
  }
2870
- if (exists(join$1(workspacePath, 'package-lock.json'))) {
2871
- confidence += 10;
2872
- sources.push({ type: 'lockfile', path: 'package-lock.json' });
2824
+ const deps = collectAllDependencies(pkg);
2825
+ if (deps['turbo']) {
2826
+ confidence += 15;
2827
+ version = parseVersionString(deps['turbo']);
2828
+ sources.push({ type: 'package.json', field: 'dependencies.turbo' });
2873
2829
  }
2874
- if (exists(join$1(workspacePath, 'yarn.lock'))) {
2875
- return null; // Let yarn workspace detector handle this
2830
+ const scripts = pkg?.scripts ?? {};
2831
+ if (values(scripts).some((s) => s?.includes('turbo'))) {
2832
+ confidence += 5;
2833
+ sources.push({ type: 'package.json', field: 'scripts (turbo commands)' });
2876
2834
  }
2877
2835
  if (confidence === 0) {
2878
2836
  return null;
2879
2837
  }
2880
2838
  return {
2881
- id: 'npm-workspaces',
2882
- name: 'npm Workspaces',
2883
- configPath: 'package.json',
2839
+ id: 'turborepo',
2840
+ name: 'Turborepo',
2841
+ version,
2842
+ configPath,
2884
2843
  confidence: min(confidence, 100),
2885
2844
  detectedFrom: sources,
2886
2845
  };
@@ -2892,6 +2851,23 @@ function npmWorkspacesDetector(workspacePath, packageJson) {
2892
2851
  * @param workspacePath - Workspace directory path
2893
2852
  * @param packageJson - Optional pre-loaded package.json
2894
2853
  * @returns Detection result or null if not detected
2854
+ *
2855
+ * @example Detecting yarn workspaces
2856
+ * ```typescript
2857
+ * // Project with workspaces in package.json and yarn.lock
2858
+ * const result = yarnWorkspacesDetector('/path/to/yarn-project')
2859
+ * // => {
2860
+ * // id: 'yarn-workspaces',
2861
+ * // name: 'Yarn Workspaces',
2862
+ * // confidence: 100,
2863
+ * // configPath: 'package.json',
2864
+ * // detectedFrom: [
2865
+ * // { type: 'package.json', field: 'workspaces' },
2866
+ * // { type: 'lockfile', path: 'yarn.lock' },
2867
+ * // { type: 'config-file', path: '.yarnrc.yml' }
2868
+ * // ]
2869
+ * // }
2870
+ * ```
2895
2871
  */
2896
2872
  function yarnWorkspacesDetector(workspacePath, packageJson) {
2897
2873
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
@@ -2937,6 +2913,15 @@ const monorepoDetectors = [
2937
2913
  * @param workspacePath - Workspace directory path
2938
2914
  * @param packageJson - Optional pre-loaded package.json
2939
2915
  * @returns Array of detected monorepo tools, sorted by confidence
2916
+ *
2917
+ * @example Detecting monorepo tools
2918
+ * ```typescript
2919
+ * const detections = detectMonorepoTools('/path/to/project')
2920
+ * // => [
2921
+ * // { id: 'nx', name: 'NX', confidence: 90, configPath: 'nx.json', detectedFrom: [...] },
2922
+ * // { id: 'npm-workspaces', name: 'npm Workspaces', confidence: 80, ... }
2923
+ * // ]
2924
+ * ```
2940
2925
  */
2941
2926
  function detectMonorepoTools(workspacePath, packageJson) {
2942
2927
  const pkg = packageJson ?? readPackageJsonIfExists(workspacePath);
@@ -2950,55 +2935,58 @@ function detectMonorepoTools(workspacePath, packageJson) {
2950
2935
  return results.sort((a, b) => b.confidence - a.confidence);
2951
2936
  }
2952
2937
 
2953
- /** Config patterns for Jest */
2954
- const JEST_CONFIG_PATTERNS = ['jest.config.js', 'jest.config.ts', 'jest.config.mjs', 'jest.config.cjs', 'jest.config.json'];
2938
+ /** Config patterns for Cypress */
2939
+ const CYPRESS_CONFIG_PATTERNS = ['cypress.config.js', 'cypress.config.ts', 'cypress.config.mjs', 'cypress.json'];
2955
2940
  /**
2956
- * Detect Jest in project.
2941
+ * Detect Cypress in project.
2957
2942
  *
2958
2943
  * @param projectPath - Project directory path
2959
2944
  * @param packageJson - Optional pre-loaded package.json
2960
2945
  * @returns Detection result or null if not detected
2946
+ *
2947
+ * @example Detecting Cypress testing framework
2948
+ * ```typescript
2949
+ * import { cypressDetector } from '@hyperfrontend/project-scope'
2950
+ *
2951
+ * const result = cypressDetector('./my-project')
2952
+ * if (result) {
2953
+ * console.log(`Cypress ${result.version} detected (${result.confidence}% confidence)`)
2954
+ * // => "Cypress 13.6.0 detected (95% confidence)"
2955
+ * }
2956
+ * ```
2961
2957
  */
2962
- function jestDetector(projectPath, packageJson) {
2958
+ function cypressDetector(projectPath, packageJson) {
2963
2959
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
2964
2960
  const sources = [];
2965
2961
  let confidence = 0;
2966
2962
  let version;
2967
2963
  const deps = collectAllDependencies(pkg);
2968
- if (deps['jest']) {
2964
+ if (deps['cypress']) {
2969
2965
  confidence += 60;
2970
- version = parseVersionString(deps['jest']);
2971
- sources.push({ type: 'package.json', field: 'dependencies.jest' });
2966
+ version = parseVersionString(deps['cypress']);
2967
+ sources.push({ type: 'package.json', field: 'dependencies.cypress' });
2972
2968
  }
2973
- const configPath = locateConfigFile(projectPath, JEST_CONFIG_PATTERNS);
2969
+ const configPath = locateConfigFile(projectPath, CYPRESS_CONFIG_PATTERNS);
2974
2970
  if (configPath) {
2975
2971
  confidence += 30;
2976
2972
  sources.push({ type: 'config-file', path: configPath });
2977
2973
  }
2978
- if (pkg && 'jest' in pkg) {
2979
- confidence += 20;
2980
- sources.push({ type: 'package.json', field: 'jest' });
2981
- }
2982
- const testScript = pkg?.scripts?.['test'] ?? '';
2983
- if (testScript.includes('jest')) {
2974
+ if (exists(join$1(projectPath, 'cypress'))) {
2984
2975
  confidence += 10;
2985
- sources.push({ type: 'package.json', field: 'scripts.test' });
2986
- }
2987
- if (deps['@types/jest']) {
2988
- confidence += 5;
2989
- sources.push({ type: 'package.json', field: 'dependencies.@types/jest' });
2976
+ sources.push({ type: 'directory', path: 'cypress/' });
2990
2977
  }
2991
- if (deps['ts-jest']) {
2978
+ const e2eScript = pkg?.scripts?.['e2e'] ?? pkg?.scripts?.['test:e2e'] ?? '';
2979
+ if (e2eScript.includes('cypress')) {
2992
2980
  confidence += 5;
2993
- sources.push({ type: 'package.json', field: 'dependencies.ts-jest' });
2981
+ sources.push({ type: 'package.json', field: 'scripts.e2e or scripts.test:e2e' });
2994
2982
  }
2995
2983
  if (confidence === 0) {
2996
2984
  return null;
2997
2985
  }
2998
2986
  return {
2999
- id: 'jest',
3000
- name: 'Jest',
3001
- type: 'unit',
2987
+ id: 'cypress',
2988
+ name: 'Cypress',
2989
+ type: 'e2e',
3002
2990
  version,
3003
2991
  configPath,
3004
2992
  confidence: min(confidence, 100),
@@ -3006,51 +2994,66 @@ function jestDetector(projectPath, packageJson) {
3006
2994
  };
3007
2995
  }
3008
2996
 
3009
- /** Config patterns for Vitest */
3010
- const VITEST_CONFIG_PATTERNS = ['vitest.config.js', 'vitest.config.ts', 'vitest.config.mjs'];
2997
+ /** Config patterns for Jest */
2998
+ const JEST_CONFIG_PATTERNS = ['jest.config.js', 'jest.config.ts', 'jest.config.mjs', 'jest.config.cjs', 'jest.config.json'];
3011
2999
  /**
3012
- * Detect Vitest in project.
3000
+ * Detect Jest in project.
3013
3001
  *
3014
3002
  * @param projectPath - Project directory path
3015
3003
  * @param packageJson - Optional pre-loaded package.json
3016
3004
  * @returns Detection result or null if not detected
3005
+ *
3006
+ * @example Detecting Jest testing framework
3007
+ * ```typescript
3008
+ * import { jestDetector } from '@hyperfrontend/project-scope'
3009
+ *
3010
+ * const result = jestDetector('./my-project')
3011
+ * if (result) {
3012
+ * console.log(`Jest ${result.version} detected`)
3013
+ * console.log('Sources:', result.detectedFrom.map(s => s.type))
3014
+ * // => "Sources: ['package.json', 'config-file']"
3015
+ * }
3016
+ * ```
3017
3017
  */
3018
- function vitestDetector(projectPath, packageJson) {
3018
+ function jestDetector(projectPath, packageJson) {
3019
3019
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
3020
3020
  const sources = [];
3021
3021
  let confidence = 0;
3022
3022
  let version;
3023
3023
  const deps = collectAllDependencies(pkg);
3024
- if (deps['vitest']) {
3025
- confidence += 70;
3026
- version = parseVersionString(deps['vitest']);
3027
- sources.push({ type: 'package.json', field: 'dependencies.vitest' });
3024
+ if (deps['jest']) {
3025
+ confidence += 60;
3026
+ version = parseVersionString(deps['jest']);
3027
+ sources.push({ type: 'package.json', field: 'dependencies.jest' });
3028
3028
  }
3029
- const configPath = locateConfigFile(projectPath, VITEST_CONFIG_PATTERNS);
3029
+ const configPath = locateConfigFile(projectPath, JEST_CONFIG_PATTERNS);
3030
3030
  if (configPath) {
3031
- confidence += 25;
3031
+ confidence += 30;
3032
3032
  sources.push({ type: 'config-file', path: configPath });
3033
3033
  }
3034
- if (!configPath) {
3035
- const viteConfig = exists(join$1(projectPath, 'vite.config.ts')) ||
3036
- exists(join$1(projectPath, 'vite.config.js')) ||
3037
- exists(join$1(projectPath, 'vite.config.mjs'));
3038
- if (viteConfig && deps['vitest']) {
3039
- confidence += 5;
3040
- sources.push({ type: 'config-file', path: 'vite.config.*' });
3041
- }
3034
+ if (pkg && 'jest' in pkg) {
3035
+ confidence += 20;
3036
+ sources.push({ type: 'package.json', field: 'jest' });
3042
3037
  }
3043
3038
  const testScript = pkg?.scripts?.['test'] ?? '';
3044
- if (testScript.includes('vitest')) {
3039
+ if (testScript.includes('jest')) {
3045
3040
  confidence += 10;
3046
3041
  sources.push({ type: 'package.json', field: 'scripts.test' });
3047
3042
  }
3043
+ if (deps['@types/jest']) {
3044
+ confidence += 5;
3045
+ sources.push({ type: 'package.json', field: 'dependencies.@types/jest' });
3046
+ }
3047
+ if (deps['ts-jest']) {
3048
+ confidence += 5;
3049
+ sources.push({ type: 'package.json', field: 'dependencies.ts-jest' });
3050
+ }
3048
3051
  if (confidence === 0) {
3049
3052
  return null;
3050
3053
  }
3051
3054
  return {
3052
- id: 'vitest',
3053
- name: 'Vitest',
3055
+ id: 'jest',
3056
+ name: 'Jest',
3054
3057
  type: 'unit',
3055
3058
  version,
3056
3059
  configPath,
@@ -3067,6 +3070,17 @@ const MOCHA_CONFIG_PATTERNS = ['.mocharc.js', '.mocharc.json', '.mocharc.yaml',
3067
3070
  * @param projectPath - Project directory path
3068
3071
  * @param packageJson - Optional pre-loaded package.json
3069
3072
  * @returns Detection result or null if not detected
3073
+ *
3074
+ * @example Detecting Mocha testing framework
3075
+ * ```typescript
3076
+ * import { mochaDetector } from '@hyperfrontend/project-scope'
3077
+ *
3078
+ * const result = mochaDetector('./my-project')
3079
+ * if (result) {
3080
+ * console.log(`Mocha ${result.version} detected (${result.confidence}%)`)
3081
+ * // => "Mocha 10.2.0 detected (95%)"
3082
+ * }
3083
+ * ```
3070
3084
  */
3071
3085
  function mochaDetector(projectPath, packageJson) {
3072
3086
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -3111,37 +3125,53 @@ function mochaDetector(projectPath, packageJson) {
3111
3125
  };
3112
3126
  }
3113
3127
 
3114
- /** Config patterns for Cypress */
3115
- const CYPRESS_CONFIG_PATTERNS = ['cypress.config.js', 'cypress.config.ts', 'cypress.config.mjs', 'cypress.json'];
3128
+ /** Config patterns for Playwright */
3129
+ const PLAYWRIGHT_CONFIG_PATTERNS = ['playwright.config.js', 'playwright.config.ts', 'playwright.config.mjs'];
3116
3130
  /**
3117
- * Detect Cypress in project.
3131
+ * Detect Playwright in project.
3118
3132
  *
3119
3133
  * @param projectPath - Project directory path
3120
3134
  * @param packageJson - Optional pre-loaded package.json
3121
3135
  * @returns Detection result or null if not detected
3136
+ *
3137
+ * @example Detecting Playwright testing framework
3138
+ * ```typescript
3139
+ * import { playwrightDetector } from '@hyperfrontend/project-scope'
3140
+ *
3141
+ * const result = playwrightDetector('./my-project')
3142
+ * if (result) {
3143
+ * console.log(`Playwright ${result.version} (${result.type} tests)`)
3144
+ * // => "Playwright 1.42.0 (e2e tests)"
3145
+ * }
3146
+ * ```
3122
3147
  */
3123
- function cypressDetector(projectPath, packageJson) {
3148
+ function playwrightDetector(projectPath, packageJson) {
3124
3149
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
3125
3150
  const sources = [];
3126
3151
  let confidence = 0;
3127
3152
  let version;
3128
3153
  const deps = collectAllDependencies(pkg);
3129
- if (deps['cypress']) {
3130
- confidence += 60;
3131
- version = parseVersionString(deps['cypress']);
3132
- sources.push({ type: 'package.json', field: 'dependencies.cypress' });
3154
+ if (deps['@playwright/test']) {
3155
+ confidence += 70;
3156
+ version = parseVersionString(deps['@playwright/test']);
3157
+ sources.push({ type: 'package.json', field: 'dependencies.@playwright/test' });
3133
3158
  }
3134
- const configPath = locateConfigFile(projectPath, CYPRESS_CONFIG_PATTERNS);
3159
+ if (deps['playwright']) {
3160
+ confidence += 50;
3161
+ version = version ?? parseVersionString(deps['playwright']);
3162
+ sources.push({ type: 'package.json', field: 'dependencies.playwright' });
3163
+ }
3164
+ const configPath = locateConfigFile(projectPath, PLAYWRIGHT_CONFIG_PATTERNS);
3135
3165
  if (configPath) {
3136
- confidence += 30;
3166
+ confidence += 25;
3137
3167
  sources.push({ type: 'config-file', path: configPath });
3138
3168
  }
3139
- if (exists(join$1(projectPath, 'cypress'))) {
3140
- confidence += 10;
3141
- sources.push({ type: 'directory', path: 'cypress/' });
3169
+ if (exists(join$1(projectPath, 'e2e')) || exists(join$1(projectPath, 'tests'))) {
3170
+ confidence += 5;
3171
+ sources.push({ type: 'directory', path: 'e2e/ or tests/' });
3142
3172
  }
3143
3173
  const e2eScript = pkg?.scripts?.['e2e'] ?? pkg?.scripts?.['test:e2e'] ?? '';
3144
- if (e2eScript.includes('cypress')) {
3174
+ if (e2eScript.includes('playwright')) {
3145
3175
  confidence += 5;
3146
3176
  sources.push({ type: 'package.json', field: 'scripts.e2e or scripts.test:e2e' });
3147
3177
  }
@@ -3149,8 +3179,8 @@ function cypressDetector(projectPath, packageJson) {
3149
3179
  return null;
3150
3180
  }
3151
3181
  return {
3152
- id: 'cypress',
3153
- name: 'Cypress',
3182
+ id: 'playwright',
3183
+ name: 'Playwright',
3154
3184
  type: 'e2e',
3155
3185
  version,
3156
3186
  configPath,
@@ -3159,52 +3189,64 @@ function cypressDetector(projectPath, packageJson) {
3159
3189
  };
3160
3190
  }
3161
3191
 
3162
- /** Config patterns for Playwright */
3163
- const PLAYWRIGHT_CONFIG_PATTERNS = ['playwright.config.js', 'playwright.config.ts', 'playwright.config.mjs'];
3192
+ /** Config patterns for Vitest */
3193
+ const VITEST_CONFIG_PATTERNS = ['vitest.config.js', 'vitest.config.ts', 'vitest.config.mjs'];
3164
3194
  /**
3165
- * Detect Playwright in project.
3195
+ * Detect Vitest in project.
3166
3196
  *
3167
3197
  * @param projectPath - Project directory path
3168
3198
  * @param packageJson - Optional pre-loaded package.json
3169
3199
  * @returns Detection result or null if not detected
3200
+ *
3201
+ * @example Detecting Vitest testing framework
3202
+ * ```typescript
3203
+ * import { vitestDetector } from '@hyperfrontend/project-scope'
3204
+ *
3205
+ * const result = vitestDetector('./my-project')
3206
+ * if (result) {
3207
+ * console.log(`Vitest ${result.version} detected`)
3208
+ * console.log('Config:', result.configPath)
3209
+ * // => "Config: vitest.config.ts"
3210
+ * }
3211
+ * ```
3170
3212
  */
3171
- function playwrightDetector(projectPath, packageJson) {
3213
+ function vitestDetector(projectPath, packageJson) {
3172
3214
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
3173
3215
  const sources = [];
3174
3216
  let confidence = 0;
3175
3217
  let version;
3176
3218
  const deps = collectAllDependencies(pkg);
3177
- if (deps['@playwright/test']) {
3219
+ if (deps['vitest']) {
3178
3220
  confidence += 70;
3179
- version = parseVersionString(deps['@playwright/test']);
3180
- sources.push({ type: 'package.json', field: 'dependencies.@playwright/test' });
3181
- }
3182
- if (deps['playwright']) {
3183
- confidence += 50;
3184
- version = version ?? parseVersionString(deps['playwright']);
3185
- sources.push({ type: 'package.json', field: 'dependencies.playwright' });
3221
+ version = parseVersionString(deps['vitest']);
3222
+ sources.push({ type: 'package.json', field: 'dependencies.vitest' });
3186
3223
  }
3187
- const configPath = locateConfigFile(projectPath, PLAYWRIGHT_CONFIG_PATTERNS);
3224
+ const configPath = locateConfigFile(projectPath, VITEST_CONFIG_PATTERNS);
3188
3225
  if (configPath) {
3189
3226
  confidence += 25;
3190
3227
  sources.push({ type: 'config-file', path: configPath });
3191
3228
  }
3192
- if (exists(join$1(projectPath, 'e2e')) || exists(join$1(projectPath, 'tests'))) {
3193
- confidence += 5;
3194
- sources.push({ type: 'directory', path: 'e2e/ or tests/' });
3229
+ if (!configPath) {
3230
+ const viteConfig = exists(join$1(projectPath, 'vite.config.ts')) ||
3231
+ exists(join$1(projectPath, 'vite.config.js')) ||
3232
+ exists(join$1(projectPath, 'vite.config.mjs'));
3233
+ if (viteConfig && deps['vitest']) {
3234
+ confidence += 5;
3235
+ sources.push({ type: 'config-file', path: 'vite.config.*' });
3236
+ }
3195
3237
  }
3196
- const e2eScript = pkg?.scripts?.['e2e'] ?? pkg?.scripts?.['test:e2e'] ?? '';
3197
- if (e2eScript.includes('playwright')) {
3198
- confidence += 5;
3199
- sources.push({ type: 'package.json', field: 'scripts.e2e or scripts.test:e2e' });
3238
+ const testScript = pkg?.scripts?.['test'] ?? '';
3239
+ if (testScript.includes('vitest')) {
3240
+ confidence += 10;
3241
+ sources.push({ type: 'package.json', field: 'scripts.test' });
3200
3242
  }
3201
3243
  if (confidence === 0) {
3202
3244
  return null;
3203
3245
  }
3204
3246
  return {
3205
- id: 'playwright',
3206
- name: 'Playwright',
3207
- type: 'e2e',
3247
+ id: 'vitest',
3248
+ name: 'Vitest',
3249
+ type: 'unit',
3208
3250
  version,
3209
3251
  configPath,
3210
3252
  confidence: min(confidence, 100),
@@ -3226,6 +3268,21 @@ const testingDetectors = [
3226
3268
  * @param projectPath - Project directory path
3227
3269
  * @param packageJson - Optional pre-loaded package.json
3228
3270
  * @returns Array of detected testing frameworks, sorted by confidence
3271
+ *
3272
+ * @example Detecting multiple testing frameworks
3273
+ * ```typescript
3274
+ * import { detectTestingFrameworks } from '@hyperfrontend/project-scope'
3275
+ *
3276
+ * const frameworks = detectTestingFrameworks('./my-project')
3277
+ * // => [
3278
+ * // { id: 'jest', name: 'Jest', type: 'unit', confidence: 95, ... },
3279
+ * // { id: 'cypress', name: 'Cypress', type: 'e2e', confidence: 85, ... }
3280
+ * // ]
3281
+ *
3282
+ * // Results are sorted by confidence (highest first)
3283
+ * const primary = frameworks[0]?.name ?? 'None'
3284
+ * console.log(`Primary testing framework: ${primary}`)
3285
+ * ```
3229
3286
  */
3230
3287
  function detectTestingFrameworks(projectPath, packageJson) {
3231
3288
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -3251,7 +3308,6 @@ function checkTsConfigStrict(projectPath) {
3251
3308
  if (!content)
3252
3309
  return undefined;
3253
3310
  try {
3254
- // Simple JSON parsing - doesn't handle comments but good enough for strict check
3255
3311
  const cleanContent = content.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
3256
3312
  const parsed = parse(cleanContent);
3257
3313
  return parsed?.compilerOptions?.strict === true;
@@ -3266,6 +3322,19 @@ function checkTsConfigStrict(projectPath) {
3266
3322
  * @param projectPath - Project directory path
3267
3323
  * @param packageJson - Optional pre-loaded package.json
3268
3324
  * @returns Detection result or null if not detected
3325
+ *
3326
+ * @example Detecting TypeScript
3327
+ * ```typescript
3328
+ * import { typescriptDetector } from '@hyperfrontend/project-scope'
3329
+ *
3330
+ * const result = typescriptDetector('./my-project')
3331
+ * if (result) {
3332
+ * console.log(`TypeScript ${result.version}`)
3333
+ * console.log(`Strict mode: ${result.strictMode ?? 'unknown'}`)
3334
+ * // => "TypeScript 5.3.0"
3335
+ * // => "Strict mode: true"
3336
+ * }
3337
+ * ```
3269
3338
  */
3270
3339
  function typescriptDetector(projectPath, packageJson) {
3271
3340
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -3274,19 +3343,16 @@ function typescriptDetector(projectPath, packageJson) {
3274
3343
  let configPath;
3275
3344
  let version;
3276
3345
  const deps = collectAllDependencies(pkg);
3277
- // TypeScript package
3278
3346
  if (deps['typescript']) {
3279
3347
  confidence += 50;
3280
3348
  version = parseVersionString(deps['typescript']);
3281
3349
  sources.push({ type: 'package.json', field: 'dependencies.typescript' });
3282
3350
  }
3283
- // tsconfig.json
3284
3351
  if (exists(join$1(projectPath, 'tsconfig.json'))) {
3285
3352
  confidence += 40;
3286
3353
  configPath = 'tsconfig.json';
3287
3354
  sources.push({ type: 'config-file', path: 'tsconfig.json' });
3288
3355
  }
3289
- // tsconfig.*.json variants
3290
3356
  const tsconfigVariants = ['tsconfig.build.json', 'tsconfig.lib.json', 'tsconfig.spec.json', 'tsconfig.app.json'];
3291
3357
  for (const variant of tsconfigVariants) {
3292
3358
  if (exists(join$1(projectPath, variant))) {
@@ -3295,7 +3361,6 @@ function typescriptDetector(projectPath, packageJson) {
3295
3361
  break;
3296
3362
  }
3297
3363
  }
3298
- // @types packages
3299
3364
  const typePackages = keys(deps).filter((d) => d.startsWith('@types/'));
3300
3365
  if (typePackages.length > 0) {
3301
3366
  confidence += 10;
@@ -3321,6 +3386,17 @@ function typescriptDetector(projectPath, packageJson) {
3321
3386
  * @param projectPath - Project directory path
3322
3387
  * @param packageJson - Optional pre-loaded package.json
3323
3388
  * @returns Detection result or null if not detected
3389
+ *
3390
+ * @example Detecting Flow type system
3391
+ * ```typescript
3392
+ * import { flowDetector } from '@hyperfrontend/project-scope'
3393
+ *
3394
+ * const result = flowDetector('./my-project')
3395
+ * if (result) {
3396
+ * console.log(`Flow ${result.version} with config: ${result.configPath}`)
3397
+ * // => "Flow 0.232.0 with config: .flowconfig"
3398
+ * }
3399
+ * ```
3324
3400
  */
3325
3401
  function flowDetector(projectPath, packageJson) {
3326
3402
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -3329,24 +3405,20 @@ function flowDetector(projectPath, packageJson) {
3329
3405
  let configPath;
3330
3406
  let version;
3331
3407
  const deps = collectAllDependencies(pkg);
3332
- // flow-bin package
3333
3408
  if (deps['flow-bin']) {
3334
3409
  confidence += 60;
3335
3410
  version = parseVersionString(deps['flow-bin']);
3336
3411
  sources.push({ type: 'package.json', field: 'dependencies.flow-bin' });
3337
3412
  }
3338
- // .flowconfig
3339
3413
  if (exists(join$1(projectPath, '.flowconfig'))) {
3340
3414
  confidence += 40;
3341
3415
  configPath = '.flowconfig';
3342
3416
  sources.push({ type: 'config-file', path: '.flowconfig' });
3343
3417
  }
3344
- // flow-typed directory
3345
3418
  if (exists(join$1(projectPath, 'flow-typed'))) {
3346
3419
  confidence += 10;
3347
3420
  sources.push({ type: 'directory', path: 'flow-typed/' });
3348
3421
  }
3349
- // @babel/preset-flow
3350
3422
  if (deps['@babel/preset-flow']) {
3351
3423
  confidence += 10;
3352
3424
  sources.push({ type: 'package.json', field: 'dependencies.@babel/preset-flow' });
@@ -3370,7 +3442,6 @@ function flowDetector(projectPath, packageJson) {
3370
3442
  * @returns `true` if the content contains JSDoc type annotations.
3371
3443
  */
3372
3444
  function hasJsDocTypes(content) {
3373
- // Check for JSDoc type annotations
3374
3445
  return (content.includes('@type {') ||
3375
3446
  content.includes('@param {') ||
3376
3447
  content.includes('@returns {') ||
@@ -3383,20 +3454,29 @@ function hasJsDocTypes(content) {
3383
3454
  * @param projectPath - Project directory path
3384
3455
  * @param packageJson - Optional pre-loaded package.json
3385
3456
  * @returns Detection result or null if not detected
3457
+ *
3458
+ * @example Detecting JSDoc type annotations
3459
+ * ```typescript
3460
+ * import { jsdocDetector } from '@hyperfrontend/project-scope'
3461
+ *
3462
+ * const result = jsdocDetector('./my-project')
3463
+ * if (result) {
3464
+ * console.log('JSDoc types detected')
3465
+ * console.log('Sources:', result.detectedFrom.map(s => s.path ?? s.field))
3466
+ * // => "Sources: ['jsconfig.json', 'src/utils.js (JSDoc annotations)']"
3467
+ * }
3468
+ * ```
3386
3469
  */
3387
3470
  function jsdocDetector(projectPath, packageJson) {
3388
3471
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
3389
3472
  const sources = [];
3390
3473
  let confidence = 0;
3391
3474
  const deps = collectAllDependencies(pkg);
3392
- // jsdoc package
3393
3475
  if (deps['jsdoc']) {
3394
3476
  confidence += 30;
3395
3477
  sources.push({ type: 'package.json', field: 'dependencies.jsdoc' });
3396
3478
  }
3397
- // typescript with checkJs (JSDoc type checking)
3398
3479
  if (deps['typescript']) {
3399
- // Check if checkJs is enabled in tsconfig
3400
3480
  const tsconfigPath = join$1(projectPath, 'tsconfig.json');
3401
3481
  const content = readFileIfExists(tsconfigPath);
3402
3482
  if (content) {
@@ -3413,12 +3493,10 @@ function jsdocDetector(projectPath, packageJson) {
3413
3493
  }
3414
3494
  }
3415
3495
  }
3416
- // Check for jsconfig.json (VS Code JS type checking)
3417
3496
  if (exists(join$1(projectPath, 'jsconfig.json'))) {
3418
3497
  confidence += 40;
3419
3498
  sources.push({ type: 'config-file', path: 'jsconfig.json' });
3420
3499
  }
3421
- // Sample check for JSDoc annotations in source files
3422
3500
  const srcDir = join$1(projectPath, 'src');
3423
3501
  if (exists(srcDir)) {
3424
3502
  try {
@@ -3459,6 +3537,20 @@ const typeSystemDetectors = [
3459
3537
  * @param projectPath - Project directory path
3460
3538
  * @param packageJson - Optional pre-loaded package.json
3461
3539
  * @returns Array of detected type systems, sorted by confidence
3540
+ *
3541
+ * @example Detecting all type systems
3542
+ * ```typescript
3543
+ * import { detectTypeSystems } from '@hyperfrontend/project-scope'
3544
+ *
3545
+ * const typeSystems = detectTypeSystems('./my-project')
3546
+ * // => [
3547
+ * // { id: 'typescript', name: 'TypeScript', version: '5.3.0', strictMode: true, confidence: 95, ... },
3548
+ * // { id: 'jsdoc', name: 'JSDoc', confidence: 40, ... }
3549
+ * // ]
3550
+ *
3551
+ * const primary = typeSystems[0]?.name ?? 'None'
3552
+ * console.log(`Primary type system: ${primary}`)
3553
+ * ```
3462
3554
  */
3463
3555
  function detectTypeSystems(projectPath, packageJson) {
3464
3556
  const pkg = packageJson ?? readPackageJsonIfExists(projectPath);
@@ -3500,8 +3592,6 @@ const allDetectors = {
3500
3592
  function isDetectAllOptions(value) {
3501
3593
  if (typeof value !== 'object' || value === null)
3502
3594
  return false;
3503
- // DetectAllOptions has skipCache or packageJson fields specifically
3504
- // PackageJson never has skipCache field
3505
3595
  return 'skipCache' in value || 'packageJson' in value;
3506
3596
  }
3507
3597
  /**
@@ -3514,7 +3604,7 @@ function isDetectAllOptions(value) {
3514
3604
  * @param packageJsonOrOptions - Optional pre-loaded package.json or options object
3515
3605
  * @returns All detection results organized by category
3516
3606
  *
3517
- * @example
3607
+ * @example Running all tech detectors
3518
3608
  * ```typescript
3519
3609
  * import { detectAll } from '@hyperfrontend/project-scope'
3520
3610
  *
@@ -3533,9 +3623,7 @@ function isDetectAllOptions(value) {
3533
3623
  * ```
3534
3624
  */
3535
3625
  function detectAll(projectPath, packageJsonOrOptions) {
3536
- // Handle backward-compatible arguments
3537
3626
  const options = isDetectAllOptions(packageJsonOrOptions) ? packageJsonOrOptions : { packageJson: packageJsonOrOptions };
3538
- // Check cache first (unless skipCache is true)
3539
3627
  if (!options.skipCache) {
3540
3628
  const cached = detectAllCache.get(projectPath);
3541
3629
  if (cached) {
@@ -3586,7 +3674,6 @@ function detectAll(projectPath, packageJsonOrOptions) {
3586
3674
  legacyFrameworks: result.legacyFrameworks.map((f) => f.id),
3587
3675
  testingFrameworks: result.testingFrameworks.map((f) => f.id),
3588
3676
  });
3589
- // Cache the result
3590
3677
  detectAllCache.set(projectPath, result);
3591
3678
  return result;
3592
3679
  }
@@ -3594,10 +3681,21 @@ function detectAll(projectPath, packageJsonOrOptions) {
3594
3681
  * Clear the tech detection cache.
3595
3682
  *
3596
3683
  * Useful for testing or when the project files have changed.
3684
+ *
3685
+ * @example Clearing the tech detection cache
3686
+ * ```typescript
3687
+ * import { detectAll, clearTechDetectionCache } from '@hyperfrontend/project-scope'
3688
+ *
3689
+ * // Initial detection (cached)
3690
+ * const first = detectAll('./my-project')
3691
+ *
3692
+ * // After modifying package.json, clear cache to re-detect
3693
+ * clearTechDetectionCache()
3694
+ * const fresh = detectAll('./my-project')
3695
+ * ```
3597
3696
  */
3598
3697
  function clearTechDetectionCache() {
3599
3698
  detectAllCache.clear();
3600
3699
  }
3601
3700
 
3602
3701
  export { BABEL_CONFIG_PATTERNS, CYPRESS_CONFIG_PATTERNS, ESLINT_CONFIG_PATTERNS, JEST_CONFIG_PATTERNS, MOCHA_CONFIG_PATTERNS, PARCEL_CONFIG_PATTERNS, PLAYWRIGHT_CONFIG_PATTERNS, PRETTIER_CONFIG_PATTERNS, ROLLUP_CONFIG_PATTERNS, STYLELINT_CONFIG_PATTERNS, SWC_CONFIG_PATTERNS, VITEST_CONFIG_PATTERNS, VITE_CONFIG_PATTERNS, WEBPACK_CONFIG_PATTERNS, allDetectors, angularDetector, angularJSDetector, astroDetector, babelDetector, backboneDetector, backendDetectors, biomeDetector, buildToolDetectors, clearTechDetectionCache, cypressDetector, detectAll, detectBackendFrameworks, detectBuildTools, detectFrontendFrameworks, detectLegacyFrameworks, detectLintingTools, detectMonorepoTools, detectTestingFrameworks, detectTypeSystems, emberDetector, esbuildDetector, eslintDetector, expressDetector, fastifyDetector, flowDetector, frameworkDetectors, gatsbyDetector, honoDetector, jestDetector, jqueryDetector, jsdocDetector, koaDetector, legacyDetectors, lernaDetector, lintingDetectors, mochaDetector, monorepoDetectors, nestDetector, nextjsDetector, npmWorkspacesDetector, nuxtDetector, nxDetector, parcelDetector, playwrightDetector, pnpmWorkspacesDetector, prettierDetector, qwikDetector, reactDetector, remixDetector, rollupDetector, rushDetector, solidDetector, stylelintDetector, svelteDetector, sveltekitDetector, swcDetector, testingDetectors, turborepoDetector, typeSystemDetectors, typescriptDetector, viteDetector, vitestDetector, vueDetector, webpackDetector, yarnWorkspacesDetector };
3603
- //# sourceMappingURL=index.esm.js.map