@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,2 @@
1
+ export { ProjectGrid } from './ProjectGrid.js'
2
+ export type { FolioProject } from '../types.js'
@@ -0,0 +1,6 @@
1
+ function ProjectList({ children }: { children: React.ReactNode }) {
2
+ if (!children) return null
3
+ return <div data-folio-list>{children}</div>
4
+ }
5
+
6
+ export { ProjectList }
@@ -0,0 +1,2 @@
1
+ export { ProjectList } from './ProjectList.js'
2
+ export type { FolioProject } from '../types.js'
@@ -0,0 +1,117 @@
1
+ import type { FolioProject } from '../types.js'
2
+
3
+ function ProjectView({
4
+ project,
5
+ onBack,
6
+ children
7
+ }: {
8
+ project: FolioProject
9
+ onBack?: () => void
10
+ children: React.ReactNode
11
+ }) {
12
+ return (
13
+ <div data-folio-view>
14
+ {onBack && <button onClick={onBack}>Back</button>}
15
+ <h2>{project.name}</h2>
16
+ {children}
17
+ </div>
18
+ )
19
+ }
20
+
21
+ ProjectView.Section = function ProjectViewSection({
22
+ project,
23
+ name
24
+ }: {
25
+ project: FolioProject
26
+ name: string
27
+ }) {
28
+ const sectionData = project[name as keyof FolioProject]
29
+
30
+ if (!sectionData) return null
31
+
32
+ return (
33
+ <div data-folio-view-section data-folio-view-section-name={name}>
34
+ {typeof sectionData === 'string' && <>{sectionData}</>}
35
+ {Array.isArray(sectionData) &&
36
+ sectionData.map((item, index) => {
37
+ if (typeof item === 'string') {
38
+ return <span key={index} data-folio-tag>{item}</span>
39
+ }
40
+ if ('type' in item && 'text' in item) {
41
+ const struggle = item as { type: string; text: string }
42
+ return (
43
+ <div key={index} data-folio-struggle data-folio-struggle-type={struggle.type}>
44
+ {struggle.text}
45
+ </div>
46
+ )
47
+ }
48
+ if ('date' in item && ('note' in item || 'title' in item)) {
49
+ if ('note' in item) {
50
+ const timeline = item as { date: string; note: string }
51
+ return (
52
+ <div key={index}>
53
+ <span data-folio-timeline-date>{timeline.date}</span>
54
+ <span data-folio-timeline-note>{timeline.note}</span>
55
+ </div>
56
+ )
57
+ }
58
+ if ('title' in item) {
59
+ const post = item as { title: string; date: string; url?: string }
60
+ return (
61
+ <div key={index}>
62
+ <span data-folio-post-title>{post.title}</span>
63
+ <span data-folio-post-date>{post.date}</span>
64
+ {post.url && (
65
+ <a href={post.url} data-folio-post-link>
66
+ Link
67
+ </a>
68
+ )}
69
+ </div>
70
+ )
71
+ }
72
+ }
73
+ return null
74
+ })}
75
+ </div>
76
+ )
77
+ }
78
+
79
+ ProjectView.Links = function ProjectViewLinks({ project }: { project: FolioProject }) {
80
+ const links = project.links
81
+
82
+ if (!links.github && !links.live && !links.npm && !links.appStore && !links.playStore) {
83
+ return null
84
+ }
85
+
86
+ return (
87
+ <div data-folio-view-links>
88
+ {links.github && (
89
+ <a href={links.github} data-folio-link data-folio-link-type="github">
90
+ GitHub
91
+ </a>
92
+ )}
93
+ {links.live && (
94
+ <a href={links.live} data-folio-link data-folio-link-type="live">
95
+ Live
96
+ </a>
97
+ )}
98
+ {links.npm && (
99
+ <a href={links.npm} data-folio-link data-folio-link-type="npm">
100
+ npm
101
+ </a>
102
+ )}
103
+ {links.appStore && (
104
+ <a href={links.appStore} data-folio-link data-folio-link-type="app-store">
105
+ App Store
106
+ </a>
107
+ )}
108
+ {links.playStore && (
109
+ <a href={links.playStore} data-folio-link data-folio-link-type="play-store">
110
+ Play Store
111
+ </a>
112
+ )}
113
+ </div>
114
+ )
115
+ }
116
+
117
+ export { ProjectView }
@@ -0,0 +1,2 @@
1
+ export { ProjectView } from './ProjectView.js'
2
+ export type { FolioProject } from '../types.js'
@@ -0,0 +1,58 @@
1
+ export type ProjectType = 'github' | 'manual' | 'npm' | 'product-hunt' | 'hybrid'
2
+
3
+ export type ProjectStatus = 'active' | 'shipped' | 'in-progress' | 'coming-soon' | 'archived' | 'for-sale'
4
+
5
+ export interface ProjectStruggle {
6
+ type: 'warn' | 'error'
7
+ text: string
8
+ }
9
+
10
+ export interface ProjectTimelineEntry {
11
+ date: string
12
+ note: string
13
+ }
14
+
15
+ export interface ProjectPost {
16
+ title: string
17
+ date: string
18
+ url?: string
19
+ }
20
+
21
+ export interface ProjectLinks {
22
+ github?: string
23
+ live?: string
24
+ npm?: string
25
+ appStore?: string
26
+ playStore?: string
27
+ productHunt?: string
28
+ }
29
+
30
+ export interface ProjectStats {
31
+ stars?: number
32
+ forks?: number
33
+ downloads?: string
34
+ version?: string
35
+ upvotes?: number
36
+ comments?: number
37
+ }
38
+
39
+ export interface FolioProject {
40
+ id: string
41
+ type: ProjectType
42
+ status: ProjectStatus
43
+ featured: boolean
44
+ name: string
45
+ tagline: string
46
+ description: string
47
+ background: string | null
48
+ why: string | null
49
+ image: string | null
50
+ struggles: ProjectStruggle[]
51
+ timeline: ProjectTimelineEntry[]
52
+ posts: ProjectPost[]
53
+ stack: string[]
54
+ links: ProjectLinks
55
+ stats: ProjectStats | null
56
+ language: string | null
57
+ languageColor: string | null
58
+ }
@@ -0,0 +1,58 @@
1
+ export type ProjectType = 'github' | 'manual' | 'npm' | 'product-hunt' | 'hybrid'
2
+
3
+ export type ProjectStatus = 'active' | 'shipped' | 'in-progress' | 'coming-soon' | 'archived' | 'for-sale'
4
+
5
+ export interface ProjectStruggle {
6
+ type: 'warn' | 'error'
7
+ text: string
8
+ }
9
+
10
+ export interface ProjectTimelineEntry {
11
+ date: string
12
+ note: string
13
+ }
14
+
15
+ export interface ProjectPost {
16
+ title: string
17
+ date: string
18
+ url?: string
19
+ }
20
+
21
+ export interface ProjectLinks {
22
+ github?: string
23
+ live?: string
24
+ npm?: string
25
+ appStore?: string
26
+ playStore?: string
27
+ productHunt?: string
28
+ }
29
+
30
+ export interface ProjectStats {
31
+ stars?: number
32
+ forks?: number
33
+ downloads?: string
34
+ version?: string
35
+ upvotes?: number
36
+ comments?: number
37
+ }
38
+
39
+ export interface FolioProject {
40
+ id: string
41
+ type: ProjectType
42
+ status: ProjectStatus
43
+ featured: boolean
44
+ name: string
45
+ tagline: string
46
+ description: string
47
+ background: string | null
48
+ why: string | null
49
+ image: string | null
50
+ struggles: ProjectStruggle[]
51
+ timeline: ProjectTimelineEntry[]
52
+ posts: ProjectPost[]
53
+ stack: string[]
54
+ links: ProjectLinks
55
+ stats: ProjectStats | null
56
+ language: string | null
57
+ languageColor: string | null
58
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander'
4
+ import { init } from './commands/init.js'
5
+ import { add } from './commands/add.js'
6
+
7
+ const program = new Command()
8
+
9
+ program
10
+ .name('folio')
11
+ .description('CLI tool for Folio project showcase pages')
12
+ .version('0.0.0')
13
+
14
+ program
15
+ .command('init')
16
+ .description('Initialize a new Folio project')
17
+ .option('--github', 'Auto-detect projects from GitHub repos')
18
+ .action(init)
19
+
20
+ program
21
+ .command('add')
22
+ .description('Add a Folio component to your project')
23
+ .argument('<component>', 'Component name (e.g., project-card, project-view)')
24
+ .action(add)
25
+
26
+ program.parse()
@@ -0,0 +1,191 @@
1
+ import { mkdir, access, copyFile, readdir } from 'node:fs/promises'
2
+ import { resolve, dirname } from 'node:path'
3
+ import inquirer from 'inquirer'
4
+ import chalk from 'chalk'
5
+ import { fileURLToPath } from 'node:url'
6
+
7
+ interface ComponentMapping {
8
+ [key: string]: {
9
+ sourcePath: string
10
+ destName: string
11
+ }
12
+ }
13
+
14
+ const __filename = fileURLToPath(import.meta.url)
15
+ const __dirname = dirname(__filename)
16
+
17
+ const COMPONENTS: ComponentMapping = {
18
+ 'project-card': {
19
+ sourcePath: resolve(__dirname, '../cli-components/ProjectCard'),
20
+ destName: 'ProjectCard',
21
+ },
22
+ 'project-view': {
23
+ sourcePath: resolve(__dirname, '../cli-components/ProjectView'),
24
+ destName: 'ProjectView',
25
+ },
26
+ 'project-grid': {
27
+ sourcePath: resolve(__dirname, '../cli-components/ProjectGrid'),
28
+ destName: 'ProjectGrid',
29
+ },
30
+ 'project-list': {
31
+ sourcePath: resolve(__dirname, '../cli-components/ProjectList'),
32
+ destName: 'ProjectList',
33
+ },
34
+ 'featured-project': {
35
+ sourcePath: resolve(__dirname, '../cli-components/FeaturedProject'),
36
+ destName: 'FeaturedProject',
37
+ },
38
+ }
39
+
40
+ export async function add(componentName: string): Promise<void> {
41
+ const workingDir = process.cwd()
42
+ const component = COMPONENTS[componentName]
43
+
44
+ if (!component) {
45
+ console.error(chalk.red(`✖ Component "${componentName}" not found`))
46
+ console.log()
47
+ console.log(chalk.bold('Available components:'))
48
+ Object.keys(COMPONENTS).forEach((name) => {
49
+ console.log(chalk.gray(` - ${name}`))
50
+ })
51
+ console.log()
52
+ console.log(chalk.gray('Usage: npx folio add <component-name>'))
53
+ process.exit(1)
54
+ }
55
+
56
+ const destDir = resolve(workingDir, 'components', 'folio', component.destName)
57
+ const folioDir = resolve(workingDir, 'components', 'folio')
58
+
59
+ console.log(chalk.bold(`📦 Adding ${componentName}...`))
60
+ console.log()
61
+
62
+ try {
63
+ const sourceFiles = await getSourceFiles(component.sourcePath)
64
+
65
+ if (sourceFiles.length === 0) {
66
+ console.error(chalk.red(`✖ No files found for ${componentName}`))
67
+ console.error(chalk.gray(' Try reinstalling @folio/cli'))
68
+ process.exit(1)
69
+ }
70
+
71
+ const existingFiles = await checkExistingFiles(destDir, sourceFiles)
72
+
73
+ if (existingFiles.length > 0) {
74
+ console.log(chalk.yellow(`⚠ Found ${existingFiles.length} existing file(s):`))
75
+ existingFiles.forEach((file) => {
76
+ console.log(chalk.gray(` - ${file}`))
77
+ })
78
+ console.log()
79
+
80
+ const { overwrite } = await inquirer.prompt([
81
+ {
82
+ type: 'confirm',
83
+ name: 'overwrite',
84
+ message: 'Overwrite existing files?',
85
+ default: false,
86
+ },
87
+ ])
88
+
89
+ if (!overwrite) {
90
+ console.log(chalk.yellow('✖ Add cancelled.'))
91
+ return
92
+ }
93
+ }
94
+
95
+ await createDirectory(destDir)
96
+ await copyFiles(sourceFiles, destDir)
97
+
98
+ const typesSource = resolve(__dirname, '../cli-components/types.ts')
99
+ const typesDest = resolve(folioDir, 'types.ts')
100
+ const typesExist = await checkTypeFileExists(typesDest)
101
+
102
+ if (!typesExist) {
103
+ await copyFile(typesSource, typesDest)
104
+ console.log(chalk.gray(' ✓ Added types.ts'))
105
+ }
106
+
107
+ console.log(chalk.green(`✓ ${componentName} added successfully`))
108
+ console.log()
109
+ console.log(chalk.bold('Import usage:'))
110
+ console.log(chalk.gray(` import { ${component.destName} } from './components/folio/${component.destName}'`))
111
+ } catch (error) {
112
+ if (error instanceof Error && 'code' in error) {
113
+ if (error.code === 'EACCES') {
114
+ console.error(chalk.red(`✖ Permission denied`))
115
+ console.error(chalk.gray(` Cannot write to ${destDir}`))
116
+ console.error(chalk.gray(' Check file permissions and try again.'))
117
+ process.exit(1)
118
+ } else if (error.code === 'ENOENT') {
119
+ console.error(chalk.red(`✖ Source files not found`))
120
+ console.error(chalk.gray(' Try reinstalling @folio/cli'))
121
+ process.exit(1)
122
+ }
123
+ }
124
+
125
+ console.error(chalk.red('✖ Failed to add component'))
126
+ console.error(chalk.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`))
127
+ process.exit(1)
128
+ }
129
+ }
130
+
131
+ async function getSourceFiles(sourcePath: string): Promise<string[]> {
132
+ try {
133
+ const files = await readdir(sourcePath)
134
+ const filePaths = files
135
+ .filter((file) => file.endsWith('.tsx') || file.endsWith('.ts'))
136
+ .map((file) => resolve(sourcePath, file))
137
+ return filePaths
138
+ } catch {
139
+ return []
140
+ }
141
+ }
142
+
143
+ async function checkExistingFiles(destDir: string, sourceFiles: string[]): Promise<string[]> {
144
+ try {
145
+ await access(destDir)
146
+ } catch {
147
+ return []
148
+ }
149
+
150
+ const existing: string[] = []
151
+
152
+ for (const sourceFile of sourceFiles) {
153
+ const fileName = sourceFile.split('/').pop() as string
154
+ const destPath = resolve(destDir, fileName)
155
+
156
+ try {
157
+ await access(destPath)
158
+ existing.push(fileName)
159
+ } catch {
160
+ }
161
+ }
162
+
163
+ return existing
164
+ }
165
+
166
+ async function checkTypeFileExists(filePath: string): Promise<boolean> {
167
+ try {
168
+ await access(filePath)
169
+ return true
170
+ } catch {
171
+ return false
172
+ }
173
+ }
174
+
175
+ async function createDirectory(dirPath: string): Promise<void> {
176
+ try {
177
+ await mkdir(dirPath, { recursive: true })
178
+ } catch (error) {
179
+ if (error instanceof Error && 'code' in error && error.code !== 'EEXIST') {
180
+ throw error
181
+ }
182
+ }
183
+ }
184
+
185
+ async function copyFiles(sourceFiles: string[], destDir: string): Promise<void> {
186
+ for (const sourceFile of sourceFiles) {
187
+ const fileName = sourceFile.split('/').pop() as string
188
+ const destPath = resolve(destDir, fileName)
189
+ await copyFile(sourceFile, destPath)
190
+ }
191
+ }