@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,222 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { sortByDate, type SortOrder } from '../sortByDate'
3
+ import type { FolioProject } from '../../types'
4
+
5
+ function createProject(overrides: Partial<FolioProject> = {}): FolioProject {
6
+ return {
7
+ id: 'test-id',
8
+ type: 'github',
9
+ status: 'active',
10
+ featured: false,
11
+ name: 'Test Project',
12
+ tagline: '',
13
+ description: '',
14
+ background: null,
15
+ why: null,
16
+ image: null,
17
+ struggles: [],
18
+ timeline: [],
19
+ posts: [],
20
+ stack: [],
21
+ links: {},
22
+ stats: null,
23
+ language: null,
24
+ languageColor: null,
25
+ createdAt: null,
26
+ updatedAt: null,
27
+ ...overrides,
28
+ }
29
+ }
30
+
31
+ describe('sortByDate', () => {
32
+ describe('empty array handling', () => {
33
+ it('should return empty array when input is empty', () => {
34
+ expect(sortByDate([])).toEqual([])
35
+ })
36
+
37
+ it('should return empty array with asc order when input is empty', () => {
38
+ expect(sortByDate([], 'asc')).toEqual([])
39
+ })
40
+ })
41
+
42
+ describe('sorting with updatedAt', () => {
43
+ it('should sort by updatedAt descending by default', () => {
44
+ const projects = [
45
+ createProject({ id: '1', updatedAt: '2024-01-01' }),
46
+ createProject({ id: '2', updatedAt: '2024-06-15' }),
47
+ createProject({ id: '3', updatedAt: '2024-03-10' }),
48
+ ]
49
+
50
+ const result = sortByDate(projects)
51
+
52
+ expect(result.map(p => p.id)).toEqual(['2', '3', '1'])
53
+ })
54
+
55
+ it('should sort by updatedAt ascending when order is asc', () => {
56
+ const projects = [
57
+ createProject({ id: '1', updatedAt: '2024-01-01' }),
58
+ createProject({ id: '2', updatedAt: '2024-06-15' }),
59
+ createProject({ id: '3', updatedAt: '2024-03-10' }),
60
+ ]
61
+
62
+ const result = sortByDate(projects, 'asc')
63
+
64
+ expect(result.map(p => p.id)).toEqual(['1', '3', '2'])
65
+ })
66
+ })
67
+
68
+ describe('fallback to createdAt', () => {
69
+ it('should fallback to createdAt when updatedAt is missing', () => {
70
+ const projects = [
71
+ createProject({ id: '1', createdAt: '2024-01-01' }),
72
+ createProject({ id: '2', updatedAt: '2024-06-15', createdAt: '2024-01-01' }),
73
+ createProject({ id: '3', createdAt: '2024-03-10' }),
74
+ ]
75
+
76
+ const result = sortByDate(projects, 'desc')
77
+
78
+ expect(result.map(p => p.id)).toEqual(['2', '3', '1'])
79
+ })
80
+
81
+ it('should use createdAt when updatedAt is null', () => {
82
+ const projects = [
83
+ createProject({ id: '1', updatedAt: null, createdAt: '2024-05-01' }),
84
+ createProject({ id: '2', updatedAt: '2024-03-01', createdAt: '2024-01-01' }),
85
+ ]
86
+
87
+ const result = sortByDate(projects, 'desc')
88
+
89
+ expect(result.map(p => p.id)).toEqual(['1', '2'])
90
+ })
91
+ })
92
+
93
+ describe('handling missing dates', () => {
94
+ it('should place projects without dates at the beginning in desc order', () => {
95
+ const projects = [
96
+ createProject({ id: '1', updatedAt: '2024-01-01' }),
97
+ createProject({ id: '2', updatedAt: null, createdAt: null }),
98
+ createProject({ id: '3', updatedAt: '2024-06-01' }),
99
+ ]
100
+
101
+ const result = sortByDate(projects, 'desc')
102
+
103
+ expect(result.map(p => p.id)).toEqual(['2', '3', '1'])
104
+ })
105
+
106
+ it('should place projects without dates at the end in asc order', () => {
107
+ const projects = [
108
+ createProject({ id: '1', updatedAt: '2024-01-01' }),
109
+ createProject({ id: '2', updatedAt: null, createdAt: null }),
110
+ createProject({ id: '3', updatedAt: '2024-06-01' }),
111
+ ]
112
+
113
+ const result = sortByDate(projects, 'asc')
114
+
115
+ expect(result.map(p => p.id)).toEqual(['1', '3', '2'])
116
+ })
117
+
118
+ it('should handle all projects without dates', () => {
119
+ const projects = [
120
+ createProject({ id: '1', updatedAt: null, createdAt: null }),
121
+ createProject({ id: '2', updatedAt: null, createdAt: null }),
122
+ ]
123
+
124
+ const result = sortByDate(projects)
125
+
126
+ expect(result).toHaveLength(2)
127
+ })
128
+
129
+ it('should return 0 for comparison when both dates are missing', () => {
130
+ const projects = [
131
+ createProject({ id: '1', updatedAt: null, createdAt: null }),
132
+ createProject({ id: '2', updatedAt: null, createdAt: null }),
133
+ ]
134
+
135
+ const result = sortByDate(projects, 'desc')
136
+
137
+ expect(result).toHaveLength(2)
138
+ })
139
+ })
140
+
141
+ describe('date parsing', () => {
142
+ it('should correctly parse ISO date strings', () => {
143
+ const projects = [
144
+ createProject({ id: '1', updatedAt: '2024-12-31T23:59:59Z' }),
145
+ createProject({ id: '2', updatedAt: '2024-01-01T00:00:00Z' }),
146
+ ]
147
+
148
+ const result = sortByDate(projects, 'desc')
149
+
150
+ expect(result.map(p => p.id)).toEqual(['1', '2'])
151
+ })
152
+
153
+ it('should handle different date formats', () => {
154
+ const projects = [
155
+ createProject({ id: '1', updatedAt: '2024-06-15' }),
156
+ createProject({ id: '2', updatedAt: '2024-03-20' }),
157
+ createProject({ id: '3', updatedAt: '2024-01-05' }),
158
+ ]
159
+
160
+ const result = sortByDate(projects, 'asc')
161
+
162
+ expect(result.map(p => p.id)).toEqual(['3', '2', '1'])
163
+ })
164
+ })
165
+
166
+ describe('immutability', () => {
167
+ it('should not mutate the original array', () => {
168
+ const projects = [
169
+ createProject({ id: '1', updatedAt: '2024-01-01' }),
170
+ createProject({ id: '2', updatedAt: '2024-06-01' }),
171
+ ]
172
+
173
+ const originalOrder = projects.map(p => p.id)
174
+ sortByDate(projects, 'desc')
175
+
176
+ expect(projects.map(p => p.id)).toEqual(originalOrder)
177
+ })
178
+
179
+ it('should return a new array instance', () => {
180
+ const projects = [createProject({ updatedAt: '2024-01-01' })]
181
+
182
+ const result = sortByDate(projects)
183
+
184
+ expect(result).not.toBe(projects)
185
+ })
186
+ })
187
+
188
+ describe('order parameter types', () => {
189
+ it('should accept asc order', () => {
190
+ const projects = [
191
+ createProject({ id: '1', updatedAt: '2024-06-01' }),
192
+ createProject({ id: '2', updatedAt: '2024-01-01' }),
193
+ ]
194
+
195
+ const result = sortByDate(projects, 'asc' as SortOrder)
196
+
197
+ expect(result.map(p => p.id)).toEqual(['2', '1'])
198
+ })
199
+
200
+ it('should accept desc order', () => {
201
+ const projects = [
202
+ createProject({ id: '1', updatedAt: '2024-01-01' }),
203
+ createProject({ id: '2', updatedAt: '2024-06-01' }),
204
+ ]
205
+
206
+ const result = sortByDate(projects, 'desc' as SortOrder)
207
+
208
+ expect(result.map(p => p.id)).toEqual(['2', '1'])
209
+ })
210
+
211
+ it('should default to desc when order is not provided', () => {
212
+ const projects = [
213
+ createProject({ id: '1', updatedAt: '2024-01-01' }),
214
+ createProject({ id: '2', updatedAt: '2024-06-01' }),
215
+ ]
216
+
217
+ const result = sortByDate(projects)
218
+
219
+ expect(result.map(p => p.id)).toEqual(['2', '1'])
220
+ })
221
+ })
222
+ })
@@ -0,0 +1,249 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { sortByName } from '../sortByName'
3
+ import type { FolioProject } from '../../types'
4
+
5
+ function createProject(overrides: Partial<FolioProject> = {}): FolioProject {
6
+ return {
7
+ id: 'test-id',
8
+ type: 'github',
9
+ status: 'active',
10
+ featured: false,
11
+ name: 'Test Project',
12
+ tagline: '',
13
+ description: '',
14
+ background: null,
15
+ why: null,
16
+ image: null,
17
+ struggles: [],
18
+ timeline: [],
19
+ posts: [],
20
+ stack: [],
21
+ links: {},
22
+ stats: null,
23
+ language: null,
24
+ languageColor: null,
25
+ createdAt: null,
26
+ updatedAt: null,
27
+ ...overrides,
28
+ }
29
+ }
30
+
31
+ describe('sortByName', () => {
32
+ describe('empty array handling', () => {
33
+ it('should return empty array when input is empty', () => {
34
+ expect(sortByName([])).toEqual([])
35
+ })
36
+
37
+ it('should return empty array with desc order when input is empty', () => {
38
+ expect(sortByName([], 'desc')).toEqual([])
39
+ })
40
+ })
41
+
42
+ describe('case-insensitive sorting', () => {
43
+ it('should sort names case-insensitively by default (asc)', () => {
44
+ const projects = [
45
+ createProject({ id: '1', name: 'zebra' }),
46
+ createProject({ id: '2', name: 'Apple' }),
47
+ createProject({ id: '3', name: 'BANANA' }),
48
+ ]
49
+
50
+ const result = sortByName(projects)
51
+
52
+ expect(result.map(p => p.id)).toEqual(['2', '3', '1'])
53
+ })
54
+
55
+ it('should sort names case-insensitively in desc order', () => {
56
+ const projects = [
57
+ createProject({ id: '1', name: 'apple' }),
58
+ createProject({ id: '2', name: 'Zebra' }),
59
+ createProject({ id: '3', name: 'Banana' }),
60
+ ]
61
+
62
+ const result = sortByName(projects, 'desc')
63
+
64
+ expect(result.map(p => p.id)).toEqual(['2', '3', '1'])
65
+ })
66
+
67
+ it('should handle mixed case names correctly', () => {
68
+ const projects = [
69
+ createProject({ id: '1', name: 'React' }),
70
+ createProject({ id: '2', name: 'react' }),
71
+ createProject({ id: '3', name: 'REACT' }),
72
+ ]
73
+
74
+ const result = sortByName(projects, 'asc')
75
+
76
+ expect(result).toHaveLength(3)
77
+ })
78
+ })
79
+
80
+ describe('handling missing names', () => {
81
+ it('should place projects with null names at the end in asc order', () => {
82
+ const projects = [
83
+ createProject({ id: '1', name: 'Beta' }),
84
+ createProject({ id: '2', name: null as unknown as string }),
85
+ createProject({ id: '3', name: 'Alpha' }),
86
+ ]
87
+
88
+ const result = sortByName(projects, 'asc')
89
+
90
+ expect(result[result.length - 1].id).toBe('2')
91
+ expect(result.slice(0, 2).map(p => p.id)).toEqual(['3', '1'])
92
+ })
93
+
94
+ it('should place projects with null names at the end in desc order', () => {
95
+ const projects = [
96
+ createProject({ id: '1', name: 'Beta' }),
97
+ createProject({ id: '2', name: null as unknown as string }),
98
+ createProject({ id: '3', name: 'Alpha' }),
99
+ ]
100
+
101
+ const result = sortByName(projects, 'desc')
102
+
103
+ expect(result[result.length - 1].id).toBe('2')
104
+ })
105
+
106
+ it('should handle all projects with null names', () => {
107
+ const projects = [
108
+ createProject({ id: '1', name: null as unknown as string }),
109
+ createProject({ id: '2', name: null as unknown as string }),
110
+ ]
111
+
112
+ const result = sortByName(projects)
113
+
114
+ expect(result).toHaveLength(2)
115
+ })
116
+
117
+ it('should return 0 for comparison when both names are null', () => {
118
+ const projects = [
119
+ createProject({ id: '1', name: null as unknown as string }),
120
+ createProject({ id: '2', name: null as unknown as string }),
121
+ ]
122
+
123
+ const result = sortByName(projects, 'asc')
124
+
125
+ expect(result).toHaveLength(2)
126
+ })
127
+ })
128
+
129
+ describe('alphabetical sorting', () => {
130
+ it('should sort names alphabetically in asc order', () => {
131
+ const projects = [
132
+ createProject({ id: '1', name: 'Zulu' }),
133
+ createProject({ id: '2', name: 'Alpha' }),
134
+ createProject({ id: '3', name: 'Mike' }),
135
+ createProject({ id: '4', name: 'Charlie' }),
136
+ ]
137
+
138
+ const result = sortByName(projects, 'asc')
139
+
140
+ expect(result.map(p => p.name)).toEqual(['Alpha', 'Charlie', 'Mike', 'Zulu'])
141
+ })
142
+
143
+ it('should sort names reverse-alphabetically in desc order', () => {
144
+ const projects = [
145
+ createProject({ id: '1', name: 'Alpha' }),
146
+ createProject({ id: '2', name: 'Zulu' }),
147
+ createProject({ id: '3', name: 'Mike' }),
148
+ ]
149
+
150
+ const result = sortByName(projects, 'desc')
151
+
152
+ expect(result.map(p => p.name)).toEqual(['Zulu', 'Mike', 'Alpha'])
153
+ })
154
+
155
+ it('should handle names with numbers (lexical sorting)', () => {
156
+ const projects = [
157
+ createProject({ id: '1', name: 'Project 2' }),
158
+ createProject({ id: '2', name: 'Project 10' }),
159
+ createProject({ id: '3', name: 'Project 1' }),
160
+ ]
161
+
162
+ const result = sortByName(projects, 'asc')
163
+
164
+ expect(result.map(p => p.id)).toEqual(['3', '2', '1'])
165
+ })
166
+ })
167
+
168
+ describe('special characters and unicode', () => {
169
+ it('should handle names with special characters', () => {
170
+ const projects = [
171
+ createProject({ id: '1', name: '@project' }),
172
+ createProject({ id: '2', name: 'A-project' }),
173
+ createProject({ id: '3', name: '_project' }),
174
+ ]
175
+
176
+ const result = sortByName(projects, 'asc')
177
+
178
+ expect(result).toHaveLength(3)
179
+ })
180
+
181
+ it('should handle unicode characters', () => {
182
+ const projects = [
183
+ createProject({ id: '1', name: 'Über' }),
184
+ createProject({ id: '2', name: 'Apple' }),
185
+ createProject({ id: '3', name: 'Äpfel' }),
186
+ ]
187
+
188
+ const result = sortByName(projects, 'asc')
189
+
190
+ expect(result.map(p => p.id)).toEqual(['3', '2', '1'])
191
+ })
192
+ })
193
+
194
+ describe('immutability', () => {
195
+ it('should not mutate the original array', () => {
196
+ const projects = [
197
+ createProject({ id: '1', name: 'Zulu' }),
198
+ createProject({ id: '2', name: 'Alpha' }),
199
+ ]
200
+
201
+ const originalOrder = projects.map(p => p.id)
202
+ sortByName(projects, 'asc')
203
+
204
+ expect(projects.map(p => p.id)).toEqual(originalOrder)
205
+ })
206
+
207
+ it('should return a new array instance', () => {
208
+ const projects = [createProject({ name: 'Test' })]
209
+
210
+ const result = sortByName(projects)
211
+
212
+ expect(result).not.toBe(projects)
213
+ })
214
+ })
215
+
216
+ describe('edge cases', () => {
217
+ it('should handle duplicate names', () => {
218
+ const projects = [
219
+ createProject({ id: '1', name: 'Project' }),
220
+ createProject({ id: '2', name: 'Project' }),
221
+ createProject({ id: '3', name: 'Project' }),
222
+ ]
223
+
224
+ const result = sortByName(projects, 'asc')
225
+
226
+ expect(result).toHaveLength(3)
227
+ })
228
+
229
+ it('should handle empty string names', () => {
230
+ const projects = [
231
+ createProject({ id: '1', name: '' }),
232
+ createProject({ id: '2', name: 'Alpha' }),
233
+ ]
234
+
235
+ const result = sortByName(projects, 'asc')
236
+
237
+ expect(result[result.length - 1].id).toBe('1')
238
+ })
239
+
240
+ it('should handle single project', () => {
241
+ const projects = [createProject({ name: 'Solo' })]
242
+
243
+ const result = sortByName(projects)
244
+
245
+ expect(result).toHaveLength(1)
246
+ expect(result[0].name).toBe('Solo')
247
+ })
248
+ })
249
+ })
@@ -0,0 +1,206 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { sortByStars } from '../sortByStars'
3
+ import type { FolioProject } from '../../types'
4
+
5
+ function createProject(overrides: Partial<FolioProject> = {}): FolioProject {
6
+ return {
7
+ id: 'test-id',
8
+ type: 'github',
9
+ status: 'active',
10
+ featured: false,
11
+ name: 'Test Project',
12
+ tagline: '',
13
+ description: '',
14
+ background: null,
15
+ why: null,
16
+ image: null,
17
+ struggles: [],
18
+ timeline: [],
19
+ posts: [],
20
+ stack: [],
21
+ links: {},
22
+ stats: null,
23
+ language: null,
24
+ languageColor: null,
25
+ createdAt: null,
26
+ updatedAt: null,
27
+ ...overrides,
28
+ }
29
+ }
30
+
31
+ describe('sortByStars', () => {
32
+ describe('empty array handling', () => {
33
+ it('should return empty array when input is empty', () => {
34
+ expect(sortByStars([])).toEqual([])
35
+ })
36
+
37
+ it('should return empty array with asc order when input is empty', () => {
38
+ expect(sortByStars([], 'asc')).toEqual([])
39
+ })
40
+ })
41
+
42
+ describe('sorting GitHub projects by stars', () => {
43
+ it('should sort GitHub projects by stars descending by default', () => {
44
+ const projects = [
45
+ createProject({ id: '1', type: 'github', stats: { stars: 100 } }),
46
+ createProject({ id: '2', type: 'github', stats: { stars: 500 } }),
47
+ createProject({ id: '3', type: 'github', stats: { stars: 250 } }),
48
+ ]
49
+
50
+ const result = sortByStars(projects)
51
+
52
+ expect(result.map(p => p.id)).toEqual(['2', '3', '1'])
53
+ })
54
+
55
+ it('should sort GitHub projects by stars ascending when order is asc', () => {
56
+ const projects = [
57
+ createProject({ id: '1', type: 'github', stats: { stars: 100 } }),
58
+ createProject({ id: '2', type: 'github', stats: { stars: 500 } }),
59
+ createProject({ id: '3', type: 'github', stats: { stars: 250 } }),
60
+ ]
61
+
62
+ const result = sortByStars(projects, 'asc')
63
+
64
+ expect(result.map(p => p.id)).toEqual(['1', '3', '2'])
65
+ })
66
+ })
67
+
68
+ describe('handling non-GitHub projects', () => {
69
+ it('should treat non-GitHub projects as having 0 stars', () => {
70
+ const projects = [
71
+ createProject({ id: '1', type: 'npm', stats: { downloads: '1000' } }),
72
+ createProject({ id: '2', type: 'github', stats: { stars: 500 } }),
73
+ createProject({ id: '3', type: 'manual' }),
74
+ ]
75
+
76
+ const result = sortByStars(projects, 'desc')
77
+
78
+ expect(result[0].id).toBe('2')
79
+ expect(result.filter(p => p.type !== 'github').every(p => result.indexOf(p) > 0)).toBe(true)
80
+ })
81
+
82
+ it('should place non-GitHub projects at the end in desc order', () => {
83
+ const projects = [
84
+ createProject({ id: '1', type: 'github', stats: { stars: 100 } }),
85
+ createProject({ id: '2', type: 'npm' }),
86
+ createProject({ id: '3', type: 'github', stats: { stars: 500 } }),
87
+ ]
88
+
89
+ const result = sortByStars(projects, 'desc')
90
+
91
+ expect(result[result.length - 1].type).toBe('npm')
92
+ })
93
+ })
94
+
95
+ describe('handling missing stats', () => {
96
+ it('should handle GitHub projects without stats', () => {
97
+ const projects = [
98
+ createProject({ id: '1', type: 'github', stats: null }),
99
+ createProject({ id: '2', type: 'github', stats: { stars: 500 } }),
100
+ ]
101
+
102
+ const result = sortByStars(projects, 'desc')
103
+
104
+ expect(result[0].id).toBe('2')
105
+ expect(result[1].id).toBe('1')
106
+ })
107
+
108
+ it('should handle GitHub projects with stats but missing stars', () => {
109
+ const projects = [
110
+ createProject({ id: '1', type: 'github', stats: { forks: 10 } }),
111
+ createProject({ id: '2', type: 'github', stats: { stars: 500 } }),
112
+ ]
113
+
114
+ const result = sortByStars(projects, 'desc')
115
+
116
+ expect(result[0].id).toBe('2')
117
+ })
118
+
119
+ it('should handle all projects with 0 stars', () => {
120
+ const projects = [
121
+ createProject({ id: '1', type: 'github', stats: { stars: 0 } }),
122
+ createProject({ id: '2', type: 'npm' }),
123
+ createProject({ id: '3', type: 'github', stats: null }),
124
+ ]
125
+
126
+ const result = sortByStars(projects, 'desc')
127
+
128
+ expect(result).toHaveLength(3)
129
+ })
130
+
131
+ it('should return 0 for comparison when both projects have 0 stars', () => {
132
+ const projects = [
133
+ createProject({ id: '1', type: 'npm' }),
134
+ createProject({ id: '2', type: 'manual' }),
135
+ ]
136
+
137
+ const result = sortByStars(projects, 'desc')
138
+
139
+ expect(result).toHaveLength(2)
140
+ })
141
+ })
142
+
143
+ describe('mixed project types', () => {
144
+ it('should correctly sort mixed project types (only github counts stars)', () => {
145
+ const projects = [
146
+ createProject({ id: '1', type: 'npm', stats: { downloads: '1000' } }),
147
+ createProject({ id: '2', type: 'github', stats: { stars: 100 } }),
148
+ createProject({ id: '3', type: 'product-hunt', stats: { upvotes: 50 } }),
149
+ createProject({ id: '4', type: 'github', stats: { stars: 500 } }),
150
+ createProject({ id: '5', type: 'hybrid', stats: { stars: 250 } }),
151
+ ]
152
+
153
+ const result = sortByStars(projects, 'desc')
154
+
155
+ expect(result[0].id).toBe('4')
156
+ expect(result[1].id).toBe('2')
157
+ expect(result.filter(p => p.type !== 'github').every(p => result.indexOf(p) > 1)).toBe(true)
158
+ })
159
+ })
160
+
161
+ describe('immutability', () => {
162
+ it('should not mutate the original array', () => {
163
+ const projects = [
164
+ createProject({ id: '1', type: 'github', stats: { stars: 100 } }),
165
+ createProject({ id: '2', type: 'github', stats: { stars: 500 } }),
166
+ ]
167
+
168
+ const originalOrder = projects.map(p => p.id)
169
+ sortByStars(projects, 'desc')
170
+
171
+ expect(projects.map(p => p.id)).toEqual(originalOrder)
172
+ })
173
+
174
+ it('should return a new array instance', () => {
175
+ const projects = [createProject({ type: 'github', stats: { stars: 100 } })]
176
+
177
+ const result = sortByStars(projects)
178
+
179
+ expect(result).not.toBe(projects)
180
+ })
181
+ })
182
+
183
+ describe('edge cases', () => {
184
+ it('should handle very large star counts', () => {
185
+ const projects = [
186
+ createProject({ id: '1', type: 'github', stats: { stars: 1000000 } }),
187
+ createProject({ id: '2', type: 'github', stats: { stars: 500 } }),
188
+ ]
189
+
190
+ const result = sortByStars(projects, 'desc')
191
+
192
+ expect(result.map(p => p.id)).toEqual(['1', '2'])
193
+ })
194
+
195
+ it('should handle projects with same star count', () => {
196
+ const projects = [
197
+ createProject({ id: '1', type: 'github', stats: { stars: 100 } }),
198
+ createProject({ id: '2', type: 'github', stats: { stars: 100 } }),
199
+ ]
200
+
201
+ const result = sortByStars(projects, 'desc')
202
+
203
+ expect(result).toHaveLength(2)
204
+ })
205
+ })
206
+ })
@@ -0,0 +1,5 @@
1
+ import type { FolioProjectInput } from '../types'
2
+
3
+ export function defineProjects(projects: FolioProjectInput[]): FolioProjectInput[] {
4
+ return projects
5
+ }
@@ -0,0 +1,16 @@
1
+ import type { FolioProject } from '../types'
2
+
3
+ export function filterByFeatured(
4
+ projects: FolioProject[],
5
+ featured: boolean | null | undefined
6
+ ): FolioProject[] {
7
+ if (projects.length === 0) {
8
+ return []
9
+ }
10
+
11
+ if (featured === null || featured === undefined) {
12
+ return [...projects]
13
+ }
14
+
15
+ return projects.filter(project => project.featured === featured)
16
+ }