@reallukemanning/folio 1.0.0

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 (332) hide show
  1. package/LICENSE +21 -0
  2. package/copy-components.js +31 -0
  3. package/coverage/base.css +224 -0
  4. package/coverage/block-navigation.js +87 -0
  5. package/coverage/core/copy-components.js.html +178 -0
  6. package/coverage/core/eslint.config.js.html +184 -0
  7. package/coverage/core/index.html +146 -0
  8. package/coverage/core/src/__tests__/benchmarks/ProjectCard.bench.tsx.html +364 -0
  9. package/coverage/core/src/__tests__/benchmarks/ProjectView.bench.tsx.html +484 -0
  10. package/coverage/core/src/__tests__/benchmarks/github.bench.ts.html +244 -0
  11. package/coverage/core/src/__tests__/benchmarks/index.html +191 -0
  12. package/coverage/core/src/__tests__/benchmarks/npm.bench.ts.html +271 -0
  13. package/coverage/core/src/__tests__/benchmarks/product-hunt.bench.ts.html +259 -0
  14. package/coverage/core/src/__tests__/benchmarks/utilities.bench.ts.html +478 -0
  15. package/coverage/core/src/components/FeaturedProject.test.tsx.html +697 -0
  16. package/coverage/core/src/components/FeaturedProject.tsx.html +163 -0
  17. package/coverage/core/src/components/ProjectCard/ProjectCard.test.tsx.html +928 -0
  18. package/coverage/core/src/components/ProjectCard/ProjectCard.tsx.html +379 -0
  19. package/coverage/core/src/components/ProjectCard/index.html +146 -0
  20. package/coverage/core/src/components/ProjectCard/index.ts.html +88 -0
  21. package/coverage/core/src/components/ProjectGrid/ProjectGrid.test.tsx.html +292 -0
  22. package/coverage/core/src/components/ProjectGrid/ProjectGrid.tsx.html +103 -0
  23. package/coverage/core/src/components/ProjectGrid/index.html +146 -0
  24. package/coverage/core/src/components/ProjectGrid/index.ts.html +88 -0
  25. package/coverage/core/src/components/ProjectList/ProjectList.test.tsx.html +292 -0
  26. package/coverage/core/src/components/ProjectList/ProjectList.tsx.html +103 -0
  27. package/coverage/core/src/components/ProjectList/index.html +146 -0
  28. package/coverage/core/src/components/ProjectList/index.ts.html +88 -0
  29. package/coverage/core/src/components/ProjectView/ProjectView.test.tsx.html +1108 -0
  30. package/coverage/core/src/components/ProjectView/ProjectView.tsx.html +589 -0
  31. package/coverage/core/src/components/ProjectView/index.html +146 -0
  32. package/coverage/core/src/components/ProjectView/index.ts.html +88 -0
  33. package/coverage/core/src/components/index.html +146 -0
  34. package/coverage/core/src/components/index.ts.html +97 -0
  35. package/coverage/core/src/index.html +116 -0
  36. package/coverage/core/src/index.ts.html +94 -0
  37. package/coverage/core/src/lib/__tests__/defineProjects.test.ts.html +421 -0
  38. package/coverage/core/src/lib/__tests__/filterByFeatured.test.ts.html +523 -0
  39. package/coverage/core/src/lib/__tests__/filterByStatus.test.ts.html +664 -0
  40. package/coverage/core/src/lib/__tests__/filterByType.test.ts.html +631 -0
  41. package/coverage/core/src/lib/__tests__/github.test.ts.html +1783 -0
  42. package/coverage/core/src/lib/__tests__/hybrid-config.test.ts.html +1345 -0
  43. package/coverage/core/src/lib/__tests__/index.html +311 -0
  44. package/coverage/core/src/lib/__tests__/normalise.test.ts.html +1585 -0
  45. package/coverage/core/src/lib/__tests__/npm-config.test.ts.html +385 -0
  46. package/coverage/core/src/lib/__tests__/npm.test.ts.html +1135 -0
  47. package/coverage/core/src/lib/__tests__/product-hunt-config.test.ts.html +397 -0
  48. package/coverage/core/src/lib/__tests__/product-hunt.test.ts.html +505 -0
  49. package/coverage/core/src/lib/__tests__/sortByDate.test.ts.html +751 -0
  50. package/coverage/core/src/lib/__tests__/sortByName.test.ts.html +832 -0
  51. package/coverage/core/src/lib/__tests__/sortByStars.test.ts.html +703 -0
  52. package/coverage/core/src/lib/defineProjects.ts.html +100 -0
  53. package/coverage/core/src/lib/filterByFeatured.ts.html +133 -0
  54. package/coverage/core/src/lib/filterByStatus.ts.html +145 -0
  55. package/coverage/core/src/lib/filterByType.ts.html +133 -0
  56. package/coverage/core/src/lib/github.ts.html +517 -0
  57. package/coverage/core/src/lib/index.html +281 -0
  58. package/coverage/core/src/lib/index.ts.html +130 -0
  59. package/coverage/core/src/lib/normalise.ts.html +868 -0
  60. package/coverage/core/src/lib/npm.ts.html +199 -0
  61. package/coverage/core/src/lib/product-hunt.ts.html +256 -0
  62. package/coverage/core/src/lib/sortByDate.ts.html +175 -0
  63. package/coverage/core/src/lib/sortByName.ts.html +172 -0
  64. package/coverage/core/src/lib/sortByStars.ts.html +172 -0
  65. package/coverage/core/src/types/index.html +116 -0
  66. package/coverage/core/src/types/index.ts.html +517 -0
  67. package/coverage/core/vitest.config.ts.html +178 -0
  68. package/coverage/coverage-final.json +53 -0
  69. package/coverage/coverage-summary.json +54 -0
  70. package/coverage/favicon.png +0 -0
  71. package/coverage/index.html +266 -0
  72. package/coverage/prettify.css +1 -0
  73. package/coverage/prettify.js +2 -0
  74. package/coverage/sort-arrow-sprite.png +0 -0
  75. package/coverage/sorter.js +210 -0
  76. package/dist/cli-components/FeaturedProject/FeaturedProject.d.ts +6 -0
  77. package/dist/cli-components/FeaturedProject/FeaturedProject.d.ts.map +1 -0
  78. package/dist/cli-components/FeaturedProject/FeaturedProject.js +9 -0
  79. package/dist/cli-components/FeaturedProject/FeaturedProject.js.map +1 -0
  80. package/dist/cli-components/FeaturedProject/FeaturedProject.tsx +54 -0
  81. package/dist/cli-components/FeaturedProject/index.d.ts +3 -0
  82. package/dist/cli-components/FeaturedProject/index.d.ts.map +1 -0
  83. package/dist/cli-components/FeaturedProject/index.js +2 -0
  84. package/dist/cli-components/FeaturedProject/index.js.map +1 -0
  85. package/dist/cli-components/FeaturedProject/index.ts +2 -0
  86. package/dist/cli-components/ProjectCard/ProjectCard.d.ts +26 -0
  87. package/dist/cli-components/ProjectCard/ProjectCard.d.ts.map +1 -0
  88. package/dist/cli-components/ProjectCard/ProjectCard.js +33 -0
  89. package/dist/cli-components/ProjectCard/ProjectCard.js.map +1 -0
  90. package/dist/cli-components/ProjectCard/ProjectCard.tsx +90 -0
  91. package/dist/cli-components/ProjectCard/index.d.ts +3 -0
  92. package/dist/cli-components/ProjectCard/index.d.ts.map +1 -0
  93. package/dist/cli-components/ProjectCard/index.js +2 -0
  94. package/dist/cli-components/ProjectCard/index.js.map +1 -0
  95. package/dist/cli-components/ProjectCard/index.ts +2 -0
  96. package/dist/cli-components/ProjectGrid/ProjectGrid.d.ts +5 -0
  97. package/dist/cli-components/ProjectGrid/ProjectGrid.d.ts.map +1 -0
  98. package/dist/cli-components/ProjectGrid/ProjectGrid.js +8 -0
  99. package/dist/cli-components/ProjectGrid/ProjectGrid.js.map +1 -0
  100. package/dist/cli-components/ProjectGrid/ProjectGrid.tsx +6 -0
  101. package/dist/cli-components/ProjectGrid/index.d.ts +3 -0
  102. package/dist/cli-components/ProjectGrid/index.d.ts.map +1 -0
  103. package/dist/cli-components/ProjectGrid/index.js +2 -0
  104. package/dist/cli-components/ProjectGrid/index.js.map +1 -0
  105. package/dist/cli-components/ProjectGrid/index.ts +2 -0
  106. package/dist/cli-components/ProjectList/ProjectList.d.ts +5 -0
  107. package/dist/cli-components/ProjectList/ProjectList.d.ts.map +1 -0
  108. package/dist/cli-components/ProjectList/ProjectList.js +8 -0
  109. package/dist/cli-components/ProjectList/ProjectList.js.map +1 -0
  110. package/dist/cli-components/ProjectList/ProjectList.tsx +6 -0
  111. package/dist/cli-components/ProjectList/index.d.ts +3 -0
  112. package/dist/cli-components/ProjectList/index.d.ts.map +1 -0
  113. package/dist/cli-components/ProjectList/index.js +2 -0
  114. package/dist/cli-components/ProjectList/index.js.map +1 -0
  115. package/dist/cli-components/ProjectList/index.ts +2 -0
  116. package/dist/cli-components/ProjectView/ProjectView.d.ts +17 -0
  117. package/dist/cli-components/ProjectView/ProjectView.d.ts.map +1 -0
  118. package/dist/cli-components/ProjectView/ProjectView.js +39 -0
  119. package/dist/cli-components/ProjectView/ProjectView.js.map +1 -0
  120. package/dist/cli-components/ProjectView/ProjectView.tsx +117 -0
  121. package/dist/cli-components/ProjectView/index.d.ts +3 -0
  122. package/dist/cli-components/ProjectView/index.d.ts.map +1 -0
  123. package/dist/cli-components/ProjectView/index.js +2 -0
  124. package/dist/cli-components/ProjectView/index.js.map +1 -0
  125. package/dist/cli-components/ProjectView/index.ts +2 -0
  126. package/dist/cli-components/types.d.ts +52 -0
  127. package/dist/cli-components/types.d.ts.map +1 -0
  128. package/dist/cli-components/types.js +2 -0
  129. package/dist/cli-components/types.js.map +1 -0
  130. package/dist/cli-components/types.ts +58 -0
  131. package/dist/cli-types.d.ts +52 -0
  132. package/dist/cli-types.d.ts.map +1 -0
  133. package/dist/cli-types.js +2 -0
  134. package/dist/cli-types.js.map +1 -0
  135. package/dist/cli.d.ts +3 -0
  136. package/dist/cli.d.ts.map +1 -0
  137. package/dist/cli.js +21 -0
  138. package/dist/cli.js.map +1 -0
  139. package/dist/commands/add.d.ts +2 -0
  140. package/dist/commands/add.d.ts.map +1 -0
  141. package/dist/commands/add.js +166 -0
  142. package/dist/commands/add.js.map +1 -0
  143. package/dist/commands/init.d.ts +6 -0
  144. package/dist/commands/init.d.ts.map +1 -0
  145. package/dist/commands/init.js +231 -0
  146. package/dist/commands/init.js.map +1 -0
  147. package/dist/components/FeaturedProject.d.ts +7 -0
  148. package/dist/components/FeaturedProject.d.ts.map +1 -0
  149. package/dist/components/FeaturedProject.js +10 -0
  150. package/dist/components/FeaturedProject.js.map +1 -0
  151. package/dist/components/FeaturedProject.test.tsx +204 -0
  152. package/dist/components/FeaturedProject.tsx +26 -0
  153. package/dist/components/ProjectCard/ProjectCard.d.ts +26 -0
  154. package/dist/components/ProjectCard/ProjectCard.d.ts.map +1 -0
  155. package/dist/components/ProjectCard/ProjectCard.js +39 -0
  156. package/dist/components/ProjectCard/ProjectCard.js.map +1 -0
  157. package/dist/components/ProjectCard/ProjectCard.test.tsx +281 -0
  158. package/dist/components/ProjectCard/ProjectCard.tsx +98 -0
  159. package/dist/components/ProjectCard/index.d.ts +2 -0
  160. package/dist/components/ProjectCard/index.d.ts.map +1 -0
  161. package/dist/components/ProjectCard/index.js +2 -0
  162. package/dist/components/ProjectCard/index.js.map +1 -0
  163. package/dist/components/ProjectCard/index.ts +1 -0
  164. package/dist/components/ProjectGrid/ProjectGrid.d.ts +5 -0
  165. package/dist/components/ProjectGrid/ProjectGrid.d.ts.map +1 -0
  166. package/dist/components/ProjectGrid/ProjectGrid.js +8 -0
  167. package/dist/components/ProjectGrid/ProjectGrid.js.map +1 -0
  168. package/dist/components/ProjectGrid/ProjectGrid.test.tsx +69 -0
  169. package/dist/components/ProjectGrid/ProjectGrid.tsx +6 -0
  170. package/dist/components/ProjectGrid/index.d.ts +2 -0
  171. package/dist/components/ProjectGrid/index.d.ts.map +1 -0
  172. package/dist/components/ProjectGrid/index.js +2 -0
  173. package/dist/components/ProjectGrid/index.js.map +1 -0
  174. package/dist/components/ProjectGrid/index.ts +1 -0
  175. package/dist/components/ProjectList/ProjectList.d.ts +5 -0
  176. package/dist/components/ProjectList/ProjectList.d.ts.map +1 -0
  177. package/dist/components/ProjectList/ProjectList.js +8 -0
  178. package/dist/components/ProjectList/ProjectList.js.map +1 -0
  179. package/dist/components/ProjectList/ProjectList.test.tsx +69 -0
  180. package/dist/components/ProjectList/ProjectList.tsx +6 -0
  181. package/dist/components/ProjectList/index.d.ts +2 -0
  182. package/dist/components/ProjectList/index.d.ts.map +1 -0
  183. package/dist/components/ProjectList/index.js +2 -0
  184. package/dist/components/ProjectList/index.js.map +1 -0
  185. package/dist/components/ProjectList/index.ts +1 -0
  186. package/dist/components/ProjectView/ProjectView.d.ts +20 -0
  187. package/dist/components/ProjectView/ProjectView.d.ts.map +1 -0
  188. package/dist/components/ProjectView/ProjectView.js +64 -0
  189. package/dist/components/ProjectView/ProjectView.js.map +1 -0
  190. package/dist/components/ProjectView/ProjectView.test.tsx +341 -0
  191. package/dist/components/ProjectView/ProjectView.tsx +168 -0
  192. package/dist/components/ProjectView/index.d.ts +2 -0
  193. package/dist/components/ProjectView/index.d.ts.map +1 -0
  194. package/dist/components/ProjectView/index.js +2 -0
  195. package/dist/components/ProjectView/index.js.map +1 -0
  196. package/dist/components/ProjectView/index.ts +1 -0
  197. package/dist/components/index.d.ts +5 -0
  198. package/dist/components/index.d.ts.map +1 -0
  199. package/dist/components/index.js +5 -0
  200. package/dist/components/index.js.map +1 -0
  201. package/dist/components/index.ts +4 -0
  202. package/dist/index.d.ts +4 -0
  203. package/dist/index.d.ts.map +1 -0
  204. package/dist/index.js +4 -0
  205. package/dist/index.js.map +1 -0
  206. package/dist/lib/defineProjects.d.ts +3 -0
  207. package/dist/lib/defineProjects.d.ts.map +1 -0
  208. package/dist/lib/defineProjects.js +4 -0
  209. package/dist/lib/defineProjects.js.map +1 -0
  210. package/dist/lib/filterByFeatured.d.ts +3 -0
  211. package/dist/lib/filterByFeatured.d.ts.map +1 -0
  212. package/dist/lib/filterByFeatured.js +10 -0
  213. package/dist/lib/filterByFeatured.js.map +1 -0
  214. package/dist/lib/filterByStatus.d.ts +3 -0
  215. package/dist/lib/filterByStatus.d.ts.map +1 -0
  216. package/dist/lib/filterByStatus.js +13 -0
  217. package/dist/lib/filterByStatus.js.map +1 -0
  218. package/dist/lib/filterByType.d.ts +3 -0
  219. package/dist/lib/filterByType.d.ts.map +1 -0
  220. package/dist/lib/filterByType.js +10 -0
  221. package/dist/lib/filterByType.js.map +1 -0
  222. package/dist/lib/github.d.ts +24 -0
  223. package/dist/lib/github.d.ts.map +1 -0
  224. package/dist/lib/github.js +107 -0
  225. package/dist/lib/github.js.map +1 -0
  226. package/dist/lib/index.d.ts +16 -0
  227. package/dist/lib/index.d.ts.map +1 -0
  228. package/dist/lib/index.js +12 -0
  229. package/dist/lib/index.js.map +1 -0
  230. package/dist/lib/normalise.d.ts +4 -0
  231. package/dist/lib/normalise.d.ts.map +1 -0
  232. package/dist/lib/normalise.js +221 -0
  233. package/dist/lib/normalise.js.map +1 -0
  234. package/dist/lib/npm.d.ts +7 -0
  235. package/dist/lib/npm.d.ts.map +1 -0
  236. package/dist/lib/npm.js +28 -0
  237. package/dist/lib/npm.js.map +1 -0
  238. package/dist/lib/product-hunt.d.ts +12 -0
  239. package/dist/lib/product-hunt.d.ts.map +1 -0
  240. package/dist/lib/product-hunt.js +40 -0
  241. package/dist/lib/product-hunt.js.map +1 -0
  242. package/dist/lib/sortByDate.d.ts +4 -0
  243. package/dist/lib/sortByDate.d.ts.map +1 -0
  244. package/dist/lib/sortByDate.js +21 -0
  245. package/dist/lib/sortByDate.js.map +1 -0
  246. package/dist/lib/sortByName.d.ts +4 -0
  247. package/dist/lib/sortByName.d.ts.map +1 -0
  248. package/dist/lib/sortByName.js +21 -0
  249. package/dist/lib/sortByName.js.map +1 -0
  250. package/dist/lib/sortByStars.d.ts +4 -0
  251. package/dist/lib/sortByStars.d.ts.map +1 -0
  252. package/dist/lib/sortByStars.js +21 -0
  253. package/dist/lib/sortByStars.js.map +1 -0
  254. package/dist/test-setup.d.ts +2 -0
  255. package/dist/test-setup.d.ts.map +1 -0
  256. package/dist/test-setup.js +2 -0
  257. package/dist/test-setup.js.map +1 -0
  258. package/dist/types/index.d.ts +121 -0
  259. package/dist/types/index.d.ts.map +1 -0
  260. package/dist/types/index.js +2 -0
  261. package/dist/types/index.js.map +1 -0
  262. package/eslint.config.js +33 -0
  263. package/package.json +47 -0
  264. package/playwright-report/index.html +85 -0
  265. package/src/__tests__/benchmarks/ProjectCard.bench.tsx +93 -0
  266. package/src/__tests__/benchmarks/ProjectView.bench.tsx +133 -0
  267. package/src/__tests__/benchmarks/github.bench.ts +53 -0
  268. package/src/__tests__/benchmarks/npm.bench.ts +62 -0
  269. package/src/__tests__/benchmarks/product-hunt.bench.ts +58 -0
  270. package/src/__tests__/benchmarks/utilities.bench.ts +131 -0
  271. package/src/cli-components/FeaturedProject/FeaturedProject.tsx +54 -0
  272. package/src/cli-components/FeaturedProject/index.ts +2 -0
  273. package/src/cli-components/ProjectCard/ProjectCard.tsx +90 -0
  274. package/src/cli-components/ProjectCard/index.ts +2 -0
  275. package/src/cli-components/ProjectGrid/ProjectGrid.tsx +6 -0
  276. package/src/cli-components/ProjectGrid/index.ts +2 -0
  277. package/src/cli-components/ProjectList/ProjectList.tsx +6 -0
  278. package/src/cli-components/ProjectList/index.ts +2 -0
  279. package/src/cli-components/ProjectView/ProjectView.tsx +117 -0
  280. package/src/cli-components/ProjectView/index.ts +2 -0
  281. package/src/cli-components/types.ts +58 -0
  282. package/src/cli-types.ts +58 -0
  283. package/src/cli.ts +26 -0
  284. package/src/commands/add.ts +191 -0
  285. package/src/commands/init.ts +260 -0
  286. package/src/components/FeaturedProject.test.tsx +204 -0
  287. package/src/components/FeaturedProject.tsx +26 -0
  288. package/src/components/ProjectCard/ProjectCard.test.tsx +281 -0
  289. package/src/components/ProjectCard/ProjectCard.tsx +98 -0
  290. package/src/components/ProjectCard/index.ts +1 -0
  291. package/src/components/ProjectGrid/ProjectGrid.test.tsx +69 -0
  292. package/src/components/ProjectGrid/ProjectGrid.tsx +6 -0
  293. package/src/components/ProjectGrid/index.ts +1 -0
  294. package/src/components/ProjectList/ProjectList.test.tsx +69 -0
  295. package/src/components/ProjectList/ProjectList.tsx +6 -0
  296. package/src/components/ProjectList/index.ts +1 -0
  297. package/src/components/ProjectView/ProjectView.test.tsx +341 -0
  298. package/src/components/ProjectView/ProjectView.tsx +168 -0
  299. package/src/components/ProjectView/index.ts +1 -0
  300. package/src/components/index.ts +4 -0
  301. package/src/index.ts +3 -0
  302. package/src/lib/__tests__/defineProjects.test.ts +112 -0
  303. package/src/lib/__tests__/filterByFeatured.test.ts +146 -0
  304. package/src/lib/__tests__/filterByStatus.test.ts +193 -0
  305. package/src/lib/__tests__/filterByType.test.ts +182 -0
  306. package/src/lib/__tests__/github.test.ts +566 -0
  307. package/src/lib/__tests__/hybrid-config.test.ts +420 -0
  308. package/src/lib/__tests__/normalise.test.ts +500 -0
  309. package/src/lib/__tests__/npm-config.test.ts +100 -0
  310. package/src/lib/__tests__/npm.test.ts +350 -0
  311. package/src/lib/__tests__/product-hunt-config.test.ts +104 -0
  312. package/src/lib/__tests__/product-hunt.test.ts +140 -0
  313. package/src/lib/__tests__/sortByDate.test.ts +222 -0
  314. package/src/lib/__tests__/sortByName.test.ts +249 -0
  315. package/src/lib/__tests__/sortByStars.test.ts +206 -0
  316. package/src/lib/defineProjects.ts +5 -0
  317. package/src/lib/filterByFeatured.ts +16 -0
  318. package/src/lib/filterByStatus.ts +20 -0
  319. package/src/lib/filterByType.ts +16 -0
  320. package/src/lib/github.ts +144 -0
  321. package/src/lib/index.ts +15 -0
  322. package/src/lib/normalise.ts +261 -0
  323. package/src/lib/npm.ts +38 -0
  324. package/src/lib/product-hunt.ts +57 -0
  325. package/src/lib/sortByDate.ts +30 -0
  326. package/src/lib/sortByName.ts +29 -0
  327. package/src/lib/sortByStars.ts +29 -0
  328. package/src/test-setup.ts +1 -0
  329. package/src/types/index.ts +144 -0
  330. package/test-results/.last-run.json +62 -0
  331. package/tsconfig.json +29 -0
  332. package/vitest.config.ts +31 -0
@@ -0,0 +1,93 @@
1
+ import { bench, describe, beforeAll } from 'vitest'
2
+ import { render } from '@testing-library/react'
3
+ import { ProjectCard } from '../../components/ProjectCard'
4
+ import type { FolioProject } from '../../types'
5
+
6
+ const createProject = (id: number): FolioProject => ({
7
+ id: `test-project-${id}`,
8
+ type: 'github',
9
+ status: 'active',
10
+ featured: false,
11
+ name: `Test Project ${id}`,
12
+ tagline: `A test project ${id}`,
13
+ description: `This is a test project description ${id} with some longer text to make it more realistic.`,
14
+ background: null,
15
+ why: null,
16
+ image: null,
17
+ struggles: [],
18
+ timeline: [],
19
+ posts: [],
20
+ stack: ['React', 'TypeScript', 'Node.js', 'CSS', 'Jest'],
21
+ links: {
22
+ github: `https://github.com/test/project-${id}`,
23
+ live: `https://test-project-${id}.com`,
24
+ npm: `https://npmjs.com/package/test-${id}`,
25
+ },
26
+ stats: {
27
+ stars: Math.floor(Math.random() * 10000),
28
+ forks: Math.floor(Math.random() * 1000),
29
+ downloads: `${Math.floor(Math.random() * 100000)}`,
30
+ version: '1.0.0',
31
+ },
32
+ language: 'TypeScript',
33
+ languageColor: '#3178c6',
34
+ createdAt: '2024-01-01',
35
+ updatedAt: '2024-06-01',
36
+ })
37
+
38
+ describe('ProjectCard render performance', () => {
39
+ let projects: FolioProject[]
40
+
41
+ beforeAll(() => {
42
+ projects = Array.from({ length: 100 }, (_, i) => createProject(i))
43
+ })
44
+
45
+ bench('render 100 ProjectCard components', () => {
46
+ for (const project of projects) {
47
+ render(
48
+ <ProjectCard>
49
+ <ProjectCard.Header project={project} />
50
+ <ProjectCard.Description project={project} />
51
+ <ProjectCard.Tags project={project} />
52
+ <ProjectCard.Stats project={project} />
53
+ <ProjectCard.Status project={project} />
54
+ <ProjectCard.Links project={project} />
55
+ </ProjectCard>
56
+ )
57
+ }
58
+ })
59
+
60
+ bench('render single ProjectCard (full composition)', () => {
61
+ const project = projects[0]
62
+ render(
63
+ <ProjectCard>
64
+ <ProjectCard.Header project={project} />
65
+ <ProjectCard.Description project={project} />
66
+ <ProjectCard.Tags project={project} />
67
+ <ProjectCard.Stats project={project} />
68
+ <ProjectCard.Status project={project} />
69
+ <ProjectCard.Links project={project} />
70
+ </ProjectCard>
71
+ )
72
+ })
73
+
74
+ bench('render ProjectCard.Header only', () => {
75
+ const project = projects[0]
76
+ render(<ProjectCard.Header project={project} />)
77
+ })
78
+
79
+ bench('render ProjectCard.Tags (5 tags)', () => {
80
+ const project = projects[0]
81
+ render(<ProjectCard.Tags project={project} />)
82
+ })
83
+
84
+ bench('render ProjectCard.Stats (4 stats)', () => {
85
+ const project = projects[0]
86
+ render(<ProjectCard.Stats project={project} />)
87
+ })
88
+
89
+ bench('render ProjectCard.Links (3 links)', () => {
90
+ const project = projects[0]
91
+ render(<ProjectCard.Links project={project} />)
92
+ })
93
+ })
@@ -0,0 +1,133 @@
1
+ import { bench, describe, beforeAll } from 'vitest'
2
+ import { render } from '@testing-library/react'
3
+ import { ProjectView } from '../../components/ProjectView'
4
+ import type { FolioProject } from '../../types'
5
+
6
+ const createProject = (id: number): FolioProject => ({
7
+ id: `test-project-${id}`,
8
+ type: 'github',
9
+ status: 'active',
10
+ featured: false,
11
+ name: `Test Project ${id}`,
12
+ tagline: `A test project ${id}`,
13
+ description: `This is a test project description ${id} with some longer text to make it more realistic.`,
14
+ background: `This is the background story for project ${id}. It explains the motivation and context.`,
15
+ why: `Why this project matters ${id}.`,
16
+ image: null,
17
+ struggles: [
18
+ { type: 'warn', text: 'First challenge faced' },
19
+ { type: 'error', text: 'Major obstacle encountered' },
20
+ ],
21
+ timeline: [
22
+ { date: '2024-01', note: 'Project started' },
23
+ { date: '2024-03', note: 'MVP completed' },
24
+ { date: '2024-06', note: 'Public launch' },
25
+ ],
26
+ posts: [
27
+ { title: 'Launch announcement', date: '2024-06-01', url: 'https://blog.example.com/launch' },
28
+ { title: 'Technical deep dive', date: '2024-07-15' },
29
+ ],
30
+ stack: ['React', 'TypeScript', 'Node.js', 'CSS', 'Jest'],
31
+ links: {
32
+ github: `https://github.com/test/project-${id}`,
33
+ live: `https://test-project-${id}.com`,
34
+ npm: `https://npmjs.com/package/test-${id}`,
35
+ },
36
+ stats: {
37
+ stars: Math.floor(Math.random() * 10000),
38
+ forks: Math.floor(Math.random() * 1000),
39
+ downloads: `${Math.floor(Math.random() * 100000)}`,
40
+ version: '1.0.0',
41
+ },
42
+ language: 'TypeScript',
43
+ languageColor: '#3178c6',
44
+ createdAt: '2024-01-01',
45
+ updatedAt: '2024-06-01',
46
+ })
47
+
48
+ describe('ProjectView render performance', () => {
49
+ let projects: FolioProject[]
50
+
51
+ beforeAll(() => {
52
+ projects = Array.from({ length: 100 }, (_, i) => createProject(i))
53
+ })
54
+
55
+ bench('render 100 ProjectView components', () => {
56
+ for (const project of projects) {
57
+ render(
58
+ <ProjectView project={project} onBack={() => {}}>
59
+ <ProjectView.Section project={project} name="background" />
60
+ <ProjectView.Section project={project} name="why" />
61
+ <ProjectView.Section project={project} name="stack" />
62
+ <ProjectView.Section project={project} name="struggles" />
63
+ <ProjectView.Section project={project} name="timeline" />
64
+ <ProjectView.Section project={project} name="posts" />
65
+ <ProjectView.Stats project={project} />
66
+ <ProjectView.Links project={project} />
67
+ </ProjectView>
68
+ )
69
+ }
70
+ })
71
+
72
+ bench('render single ProjectView (full composition)', () => {
73
+ const project = projects[0]
74
+ render(
75
+ <ProjectView project={project} onBack={() => {}}>
76
+ <ProjectView.Section project={project} name="background" />
77
+ <ProjectView.Section project={project} name="why" />
78
+ <ProjectView.Section project={project} name="stack" />
79
+ <ProjectView.Section project={project} name="struggles" />
80
+ <ProjectView.Section project={project} name="timeline" />
81
+ <ProjectView.Section project={project} name="posts" />
82
+ <ProjectView.Stats project={project} />
83
+ <ProjectView.Links project={project} />
84
+ </ProjectView>
85
+ )
86
+ })
87
+
88
+ bench('render ProjectView (no onBack)', () => {
89
+ const project = projects[0]
90
+ render(
91
+ <ProjectView project={project}>
92
+ <ProjectView.Section project={project} name="background" />
93
+ <ProjectView.Stats project={project} />
94
+ <ProjectView.Links project={project} />
95
+ </ProjectView>
96
+ )
97
+ })
98
+
99
+ bench('render ProjectView.Section (string content)', () => {
100
+ const project = projects[0]
101
+ render(<ProjectView.Section project={project} name="background" />)
102
+ })
103
+
104
+ bench('render ProjectView.Section (array - stack)', () => {
105
+ const project = projects[0]
106
+ render(<ProjectView.Section project={project} name="stack" />)
107
+ })
108
+
109
+ bench('render ProjectView.Section (array - struggles)', () => {
110
+ const project = projects[0]
111
+ render(<ProjectView.Section project={project} name="struggles" />)
112
+ })
113
+
114
+ bench('render ProjectView.Section (array - timeline)', () => {
115
+ const project = projects[0]
116
+ render(<ProjectView.Section project={project} name="timeline" />)
117
+ })
118
+
119
+ bench('render ProjectView.Section (array - posts)', () => {
120
+ const project = projects[0]
121
+ render(<ProjectView.Section project={project} name="posts" />)
122
+ })
123
+
124
+ bench('render ProjectView.Stats', () => {
125
+ const project = projects[0]
126
+ render(<ProjectView.Stats project={project} />)
127
+ })
128
+
129
+ bench('render ProjectView.Links', () => {
130
+ const project = projects[0]
131
+ render(<ProjectView.Links project={project} />)
132
+ })
133
+ })
@@ -0,0 +1,53 @@
1
+ import { bench, describe, beforeAll, vi } from 'vitest'
2
+
3
+ const mockGitHubResponse = {
4
+ name: 'folio',
5
+ description: 'A component library for project showcases',
6
+ stargazers_count: 1234,
7
+ forks_count: 56,
8
+ language: 'TypeScript',
9
+ topics: ['react', 'typescript', 'portfolio', 'components'],
10
+ html_url: 'https://github.com/test/folio',
11
+ homepage: 'https://folio.dev',
12
+ created_at: '2024-01-01T00:00:00Z',
13
+ updated_at: '2024-06-01T00:00:00Z',
14
+ }
15
+
16
+ describe('fetchGitHubRepo performance', () => {
17
+ beforeAll(() => {
18
+ vi.stubGlobal('fetch', vi.fn(async () => ({
19
+ ok: true,
20
+ json: async () => mockGitHubResponse,
21
+ })))
22
+ })
23
+
24
+ bench('fetchGitHubRepo with cache (build strategy simulation)', async () => {
25
+ const response = await fetch('https://api.github.com/repos/test/folio', {
26
+ cache: 'force-cache',
27
+ })
28
+ await response.json()
29
+ })
30
+
31
+ bench('fetchGitHubRepo without cache (runtime strategy simulation)', async () => {
32
+ const response = await fetch('https://api.github.com/repos/test/folio', {
33
+ cache: 'no-store',
34
+ })
35
+ await response.json()
36
+ })
37
+
38
+ bench('parse GitHub API response', () => {
39
+ const data = mockGitHubResponse
40
+ void {
41
+ name: data.name,
42
+ description: data.description,
43
+ stargazers_count: data.stargazers_count,
44
+ forks_count: data.forks_count,
45
+ language: data.language,
46
+ topics: data.topics || [],
47
+ html_url: data.html_url,
48
+ homepage: data.homepage,
49
+ created_at: data.created_at,
50
+ updated_at: data.updated_at,
51
+ }
52
+ })
53
+ })
@@ -0,0 +1,62 @@
1
+ import { bench, describe, beforeAll, vi } from 'vitest'
2
+
3
+ const mockDownloadsResponse = {
4
+ package: 'folio-core',
5
+ downloads: 123456,
6
+ }
7
+
8
+ const mockRegistryResponse = {
9
+ name: 'folio-core',
10
+ 'dist-tags': {
11
+ latest: '1.2.3',
12
+ },
13
+ versions: {
14
+ '1.2.3': {
15
+ version: '1.2.3',
16
+ },
17
+ },
18
+ }
19
+
20
+ describe('fetchNpmPackage performance', () => {
21
+ beforeAll(() => {
22
+ vi.stubGlobal('fetch', vi.fn(async (url: string) => {
23
+ if (url.includes('downloads')) {
24
+ return {
25
+ ok: true,
26
+ json: async () => mockDownloadsResponse,
27
+ }
28
+ }
29
+ return {
30
+ ok: true,
31
+ json: async () => mockRegistryResponse,
32
+ }
33
+ }))
34
+ })
35
+
36
+ bench('fetchNpmPackage (parallel fetch simulation)', async () => {
37
+ const [downloadsResponse, registryResponse] = await Promise.all([
38
+ fetch('https://api.npmjs.org/downloads/point/last-month/folio-core', {
39
+ cache: 'force-cache',
40
+ }),
41
+ fetch('https://registry.npmjs.org/folio-core', {
42
+ cache: 'force-cache',
43
+ }),
44
+ ])
45
+
46
+ const downloadsData = await downloadsResponse.json()
47
+ const registryData = await registryResponse.json()
48
+
49
+ void registryData['dist-tags']?.latest
50
+ void downloadsData.package
51
+ void downloadsData.downloads
52
+ })
53
+
54
+ bench('parse npm API responses', () => {
55
+ const downloadsData = mockDownloadsResponse
56
+ const registryData = mockRegistryResponse
57
+
58
+ void registryData['dist-tags']?.latest
59
+ void downloadsData.package
60
+ void downloadsData.downloads
61
+ })
62
+ })
@@ -0,0 +1,58 @@
1
+ import { bench, describe, beforeAll, vi } from 'vitest'
2
+
3
+ const mockProductHuntResponse = {
4
+ post: {
5
+ name: 'Folio',
6
+ tagline: 'Beautiful project showcases',
7
+ description: 'A component library for developers and solopreneurs building project showcase pages.',
8
+ votes_count: 456,
9
+ comments_count: 78,
10
+ featured_at: '2024-06-01T00:00:00Z',
11
+ website: 'https://folio.dev',
12
+ url: 'https://producthunt.com/posts/folio',
13
+ },
14
+ }
15
+
16
+ describe('fetchProductHuntPost performance', () => {
17
+ beforeAll(() => {
18
+ vi.stubGlobal('fetch', vi.fn(async () => ({
19
+ ok: true,
20
+ json: async () => mockProductHuntResponse,
21
+ })))
22
+ })
23
+
24
+ bench('fetchProductHuntPost (authenticated)', async () => {
25
+ const response = await fetch('https://api.producthunt.com/v2/posts/folio', {
26
+ headers: {
27
+ Authorization: 'Bearer test-token',
28
+ Accept: 'application/json',
29
+ },
30
+ cache: 'force-cache',
31
+ })
32
+ const data = await response.json()
33
+ const post = data.post
34
+
35
+ void post.name
36
+ void post.tagline
37
+ void post.description
38
+ void post.votes_count
39
+ void post.comments_count
40
+ void post.featured_at
41
+ void post.website
42
+ void post.url
43
+ })
44
+
45
+ bench('parse Product Hunt API response', () => {
46
+ const data = mockProductHuntResponse
47
+ const post = data.post
48
+
49
+ void post.name
50
+ void post.tagline
51
+ void post.description
52
+ void post.votes_count
53
+ void post.comments_count
54
+ void post.featured_at
55
+ void post.website
56
+ void post.url
57
+ })
58
+ })
@@ -0,0 +1,131 @@
1
+ import { bench, describe, beforeAll } from 'vitest'
2
+ import { filterByType } from '../../lib/filterByType'
3
+ import { filterByStatus } from '../../lib/filterByStatus'
4
+ import { filterByFeatured } from '../../lib/filterByFeatured'
5
+ import { sortByName } from '../../lib/sortByName'
6
+ import { sortByDate } from '../../lib/sortByDate'
7
+ import { sortByStars } from '../../lib/sortByStars'
8
+ import type { FolioProject, ProjectStatus, ProjectType } from '../../types'
9
+
10
+ const createProject = (id: number): FolioProject => ({
11
+ id: `project-${id}`,
12
+ type: (['github', 'npm', 'product-hunt', 'manual', 'hybrid'] as ProjectType[])[id % 5],
13
+ status: (['active', 'shipped', 'in-progress', 'archived'] as ProjectStatus[])[id % 4],
14
+ featured: id % 10 === 0,
15
+ name: `Project ${String(id).padStart(4, '0')}`,
16
+ tagline: `A test project ${id}`,
17
+ description: `Description for project ${id}`,
18
+ background: null,
19
+ why: null,
20
+ image: null,
21
+ struggles: [],
22
+ timeline: [],
23
+ posts: [],
24
+ stack: ['React', 'TypeScript', 'Node.js'],
25
+ links: {
26
+ github: `https://github.com/test/project-${id}`,
27
+ live: `https://project-${id}.com`,
28
+ },
29
+ stats: {
30
+ stars: Math.floor(Math.random() * 10000),
31
+ forks: Math.floor(Math.random() * 1000),
32
+ },
33
+ language: 'TypeScript',
34
+ languageColor: '#3178c6',
35
+ createdAt: new Date(2024, id % 12, (id % 28) + 1).toISOString(),
36
+ updatedAt: new Date(2024, (id % 12) + 1, 1).toISOString(),
37
+ })
38
+
39
+ describe('Utility functions performance (1000 projects)', () => {
40
+ let projects: FolioProject[]
41
+
42
+ beforeAll(() => {
43
+ projects = Array.from({ length: 1000 }, (_, i) => createProject(i))
44
+ })
45
+
46
+ describe('filterByType', () => {
47
+ bench('filter by github', () => {
48
+ filterByType(projects, 'github')
49
+ })
50
+
51
+ bench('filter by npm', () => {
52
+ filterByType(projects, 'npm')
53
+ })
54
+
55
+ bench('filter by all (no-op)', () => {
56
+ filterByType(projects, 'all')
57
+ })
58
+
59
+ bench('filter by undefined (no-op)', () => {
60
+ filterByType(projects, undefined)
61
+ })
62
+ })
63
+
64
+ describe('filterByStatus', () => {
65
+ bench('filter by active', () => {
66
+ filterByStatus(projects, 'active')
67
+ })
68
+
69
+ bench('filter by shipped', () => {
70
+ filterByStatus(projects, 'shipped')
71
+ })
72
+ })
73
+
74
+ describe('filterByFeatured', () => {
75
+ bench('filter featured projects', () => {
76
+ filterByFeatured(projects, true)
77
+ })
78
+
79
+ bench('filter non-featured projects', () => {
80
+ filterByFeatured(projects, false)
81
+ })
82
+ })
83
+
84
+ describe('sortByName', () => {
85
+ bench('sort by name ascending', () => {
86
+ sortByName(projects, 'asc')
87
+ })
88
+
89
+ bench('sort by name descending', () => {
90
+ sortByName(projects, 'desc')
91
+ })
92
+ })
93
+
94
+ describe('sortByDate', () => {
95
+ bench('sort by date ascending', () => {
96
+ sortByDate(projects, 'asc')
97
+ })
98
+
99
+ bench('sort by date descending', () => {
100
+ sortByDate(projects, 'desc')
101
+ })
102
+ })
103
+
104
+ describe('sortByStars', () => {
105
+ bench('sort by stars ascending', () => {
106
+ sortByStars(projects, 'asc')
107
+ })
108
+
109
+ bench('sort by stars descending', () => {
110
+ sortByStars(projects, 'desc')
111
+ })
112
+ })
113
+
114
+ describe('combined operations', () => {
115
+ bench('filter by github then sort by stars', () => {
116
+ const filtered = filterByType(projects, 'github')
117
+ sortByStars(filtered, 'desc')
118
+ })
119
+
120
+ bench('filter featured then sort by date', () => {
121
+ const featured = filterByFeatured(projects, true)
122
+ sortByDate(featured, 'desc')
123
+ })
124
+
125
+ bench('filter by status then by type then sort', () => {
126
+ const active = filterByStatus(projects, 'active')
127
+ const github = filterByType(active, 'github')
128
+ sortByStars(github, 'desc')
129
+ })
130
+ })
131
+ })
@@ -0,0 +1,54 @@
1
+ import type { FolioProject } from '../types.js'
2
+
3
+ function FeaturedProject({ project }: { project: FolioProject | null | undefined }) {
4
+ if (!project) {
5
+ return null
6
+ }
7
+
8
+ return (
9
+ <div data-folio-featured>
10
+ {project.image && <img data-folio-featured-image src={project.image} alt={project.name} />}
11
+ <div data-folio-view>
12
+ <h2>{project.name}</h2>
13
+ {project.background && <div data-folio-view-section data-folio-view-section-name="background">{project.background}</div>}
14
+ {project.why && <div data-folio-view-section data-folio-view-section-name="why">{project.why}</div>}
15
+ {project.stats && (project.stats.stars || project.stats.forks || project.stats.downloads) && (
16
+ <div data-folio-card-stats>
17
+ {project.stats.stars && <span data-folio-stat="stars">{project.stats.stars} stars</span>}
18
+ {project.stats.forks && <span data-folio-stat="forks">{project.stats.forks} forks</span>}
19
+ {project.stats.downloads && <span data-folio-stat="downloads">{project.stats.downloads} downloads</span>}
20
+ </div>
21
+ )}
22
+ <div data-folio-view-links>
23
+ {project.links.github && (
24
+ <a href={project.links.github} data-folio-link data-folio-link-type="github">
25
+ GitHub
26
+ </a>
27
+ )}
28
+ {project.links.live && (
29
+ <a href={project.links.live} data-folio-link data-folio-link-type="live">
30
+ Live
31
+ </a>
32
+ )}
33
+ {project.links.npm && (
34
+ <a href={project.links.npm} data-folio-link data-folio-link-type="npm">
35
+ npm
36
+ </a>
37
+ )}
38
+ {project.links.appStore && (
39
+ <a href={project.links.appStore} data-folio-link data-folio-link-type="app-store">
40
+ App Store
41
+ </a>
42
+ )}
43
+ {project.links.playStore && (
44
+ <a href={project.links.playStore} data-folio-link data-folio-link-type="play-store">
45
+ Play Store
46
+ </a>
47
+ )}
48
+ </div>
49
+ </div>
50
+ </div>
51
+ )
52
+ }
53
+
54
+ export { FeaturedProject }
@@ -0,0 +1,2 @@
1
+ export { FeaturedProject } from './FeaturedProject.js'
2
+ export type { FolioProject } from '../types.js'
@@ -0,0 +1,90 @@
1
+ import type { FolioProject } from '../types.js'
2
+
3
+ function ProjectCard({ children }: { children: React.ReactNode }) {
4
+ return <div data-folio-card>{children}</div>
5
+ }
6
+
7
+ ProjectCard.Header = function ProjectCardHeader({ project }: { project: FolioProject }) {
8
+ return (
9
+ <div data-folio-card-header>
10
+ <h3>{project.name}</h3>
11
+ <div data-folio-type data-folio-type-value={project.type}>
12
+ {project.type}
13
+ </div>
14
+ </div>
15
+ )
16
+ }
17
+
18
+ ProjectCard.Description = function ProjectCardDescription({ project }: { project: FolioProject }) {
19
+ if (!project.description) return null
20
+ return <div data-folio-card-description>{project.description}</div>
21
+ }
22
+
23
+ ProjectCard.Tags = function ProjectCardTags({ project }: { project: FolioProject }) {
24
+ if (!project.stack || project.stack.length === 0) return null
25
+ return (
26
+ <div data-folio-card-tags>
27
+ {project.stack.map((tag) => (
28
+ <span key={tag} data-folio-tag>
29
+ {tag}
30
+ </span>
31
+ ))}
32
+ </div>
33
+ )
34
+ }
35
+
36
+ ProjectCard.Stats = function ProjectCardStats({ project }: { project: FolioProject }) {
37
+ if (!project.stats || (!project.stats.stars && !project.stats.forks && !project.stats.downloads && !project.stats.version && !project.stats.upvotes && !project.stats.comments)) {
38
+ return null
39
+ }
40
+ return (
41
+ <div data-folio-card-stats>
42
+ {project.stats.stars && <span data-folio-stat="stars">{project.stats.stars} stars</span>}
43
+ {project.stats.forks && <span data-folio-stat="forks">{project.stats.forks} forks</span>}
44
+ {project.stats.downloads && (
45
+ <span data-folio-stat="downloads">{project.stats.downloads} downloads</span>
46
+ )}
47
+ {project.stats.version && <span data-folio-stat="version">{project.stats.version}</span>}
48
+ {project.stats.upvotes && <span data-folio-stat="upvotes">{project.stats.upvotes} upvotes</span>}
49
+ {project.stats.comments && <span data-folio-stat="comments">{project.stats.comments} comments</span>}
50
+ </div>
51
+ )
52
+ }
53
+
54
+ ProjectCard.Status = function ProjectCardStatus({ project }: { project: FolioProject }) {
55
+ return (
56
+ <div data-folio-status data-folio-status-value={project.status}>
57
+ {project.status}
58
+ </div>
59
+ )
60
+ }
61
+
62
+ ProjectCard.Links = function ProjectCardLinks({ project }: { project: FolioProject }) {
63
+ if (!project.links.github && !project.links.live && !project.links.npm && !project.links.productHunt) return null
64
+ return (
65
+ <div data-folio-card-links>
66
+ {project.links.github && (
67
+ <a href={project.links.github} data-folio-link data-folio-link-type="github">
68
+ GitHub
69
+ </a>
70
+ )}
71
+ {project.links.live && (
72
+ <a href={project.links.live} data-folio-link data-folio-link-type="live">
73
+ Live
74
+ </a>
75
+ )}
76
+ {project.links.npm && (
77
+ <a href={project.links.npm} data-folio-link data-folio-link-type="npm">
78
+ npm
79
+ </a>
80
+ )}
81
+ {project.links.productHunt && (
82
+ <a href={project.links.productHunt} data-folio-link data-folio-link-type="product-hunt">
83
+ Product Hunt
84
+ </a>
85
+ )}
86
+ </div>
87
+ )
88
+ }
89
+
90
+ export { ProjectCard }
@@ -0,0 +1,2 @@
1
+ export { ProjectCard } from './ProjectCard.js'
2
+ export type { FolioProject } from '../types.js'
@@ -0,0 +1,6 @@
1
+ function ProjectGrid({ children }: { children: React.ReactNode }) {
2
+ if (!children) return null
3
+ return <div data-folio-grid>{children}</div>
4
+ }
5
+
6
+ export { ProjectGrid }