@hyperfrontend/project-scope 0.2.0 → 0.2.2

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