@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.
- package/LICENSE +21 -0
- package/copy-components.js +31 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/core/copy-components.js.html +178 -0
- package/coverage/core/eslint.config.js.html +184 -0
- package/coverage/core/index.html +146 -0
- package/coverage/core/src/__tests__/benchmarks/ProjectCard.bench.tsx.html +364 -0
- package/coverage/core/src/__tests__/benchmarks/ProjectView.bench.tsx.html +484 -0
- package/coverage/core/src/__tests__/benchmarks/github.bench.ts.html +244 -0
- package/coverage/core/src/__tests__/benchmarks/index.html +191 -0
- package/coverage/core/src/__tests__/benchmarks/npm.bench.ts.html +271 -0
- package/coverage/core/src/__tests__/benchmarks/product-hunt.bench.ts.html +259 -0
- package/coverage/core/src/__tests__/benchmarks/utilities.bench.ts.html +478 -0
- package/coverage/core/src/components/FeaturedProject.test.tsx.html +697 -0
- package/coverage/core/src/components/FeaturedProject.tsx.html +163 -0
- package/coverage/core/src/components/ProjectCard/ProjectCard.test.tsx.html +928 -0
- package/coverage/core/src/components/ProjectCard/ProjectCard.tsx.html +379 -0
- package/coverage/core/src/components/ProjectCard/index.html +146 -0
- package/coverage/core/src/components/ProjectCard/index.ts.html +88 -0
- package/coverage/core/src/components/ProjectGrid/ProjectGrid.test.tsx.html +292 -0
- package/coverage/core/src/components/ProjectGrid/ProjectGrid.tsx.html +103 -0
- package/coverage/core/src/components/ProjectGrid/index.html +146 -0
- package/coverage/core/src/components/ProjectGrid/index.ts.html +88 -0
- package/coverage/core/src/components/ProjectList/ProjectList.test.tsx.html +292 -0
- package/coverage/core/src/components/ProjectList/ProjectList.tsx.html +103 -0
- package/coverage/core/src/components/ProjectList/index.html +146 -0
- package/coverage/core/src/components/ProjectList/index.ts.html +88 -0
- package/coverage/core/src/components/ProjectView/ProjectView.test.tsx.html +1108 -0
- package/coverage/core/src/components/ProjectView/ProjectView.tsx.html +589 -0
- package/coverage/core/src/components/ProjectView/index.html +146 -0
- package/coverage/core/src/components/ProjectView/index.ts.html +88 -0
- package/coverage/core/src/components/index.html +146 -0
- package/coverage/core/src/components/index.ts.html +97 -0
- package/coverage/core/src/index.html +116 -0
- package/coverage/core/src/index.ts.html +94 -0
- package/coverage/core/src/lib/__tests__/defineProjects.test.ts.html +421 -0
- package/coverage/core/src/lib/__tests__/filterByFeatured.test.ts.html +523 -0
- package/coverage/core/src/lib/__tests__/filterByStatus.test.ts.html +664 -0
- package/coverage/core/src/lib/__tests__/filterByType.test.ts.html +631 -0
- package/coverage/core/src/lib/__tests__/github.test.ts.html +1783 -0
- package/coverage/core/src/lib/__tests__/hybrid-config.test.ts.html +1345 -0
- package/coverage/core/src/lib/__tests__/index.html +311 -0
- package/coverage/core/src/lib/__tests__/normalise.test.ts.html +1585 -0
- package/coverage/core/src/lib/__tests__/npm-config.test.ts.html +385 -0
- package/coverage/core/src/lib/__tests__/npm.test.ts.html +1135 -0
- package/coverage/core/src/lib/__tests__/product-hunt-config.test.ts.html +397 -0
- package/coverage/core/src/lib/__tests__/product-hunt.test.ts.html +505 -0
- package/coverage/core/src/lib/__tests__/sortByDate.test.ts.html +751 -0
- package/coverage/core/src/lib/__tests__/sortByName.test.ts.html +832 -0
- package/coverage/core/src/lib/__tests__/sortByStars.test.ts.html +703 -0
- package/coverage/core/src/lib/defineProjects.ts.html +100 -0
- package/coverage/core/src/lib/filterByFeatured.ts.html +133 -0
- package/coverage/core/src/lib/filterByStatus.ts.html +145 -0
- package/coverage/core/src/lib/filterByType.ts.html +133 -0
- package/coverage/core/src/lib/github.ts.html +517 -0
- package/coverage/core/src/lib/index.html +281 -0
- package/coverage/core/src/lib/index.ts.html +130 -0
- package/coverage/core/src/lib/normalise.ts.html +868 -0
- package/coverage/core/src/lib/npm.ts.html +199 -0
- package/coverage/core/src/lib/product-hunt.ts.html +256 -0
- package/coverage/core/src/lib/sortByDate.ts.html +175 -0
- package/coverage/core/src/lib/sortByName.ts.html +172 -0
- package/coverage/core/src/lib/sortByStars.ts.html +172 -0
- package/coverage/core/src/types/index.html +116 -0
- package/coverage/core/src/types/index.ts.html +517 -0
- package/coverage/core/vitest.config.ts.html +178 -0
- package/coverage/coverage-final.json +53 -0
- package/coverage/coverage-summary.json +54 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +266 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/cli-components/FeaturedProject/FeaturedProject.d.ts +6 -0
- package/dist/cli-components/FeaturedProject/FeaturedProject.d.ts.map +1 -0
- package/dist/cli-components/FeaturedProject/FeaturedProject.js +9 -0
- package/dist/cli-components/FeaturedProject/FeaturedProject.js.map +1 -0
- package/dist/cli-components/FeaturedProject/FeaturedProject.tsx +54 -0
- package/dist/cli-components/FeaturedProject/index.d.ts +3 -0
- package/dist/cli-components/FeaturedProject/index.d.ts.map +1 -0
- package/dist/cli-components/FeaturedProject/index.js +2 -0
- package/dist/cli-components/FeaturedProject/index.js.map +1 -0
- package/dist/cli-components/FeaturedProject/index.ts +2 -0
- package/dist/cli-components/ProjectCard/ProjectCard.d.ts +26 -0
- package/dist/cli-components/ProjectCard/ProjectCard.d.ts.map +1 -0
- package/dist/cli-components/ProjectCard/ProjectCard.js +33 -0
- package/dist/cli-components/ProjectCard/ProjectCard.js.map +1 -0
- package/dist/cli-components/ProjectCard/ProjectCard.tsx +90 -0
- package/dist/cli-components/ProjectCard/index.d.ts +3 -0
- package/dist/cli-components/ProjectCard/index.d.ts.map +1 -0
- package/dist/cli-components/ProjectCard/index.js +2 -0
- package/dist/cli-components/ProjectCard/index.js.map +1 -0
- package/dist/cli-components/ProjectCard/index.ts +2 -0
- package/dist/cli-components/ProjectGrid/ProjectGrid.d.ts +5 -0
- package/dist/cli-components/ProjectGrid/ProjectGrid.d.ts.map +1 -0
- package/dist/cli-components/ProjectGrid/ProjectGrid.js +8 -0
- package/dist/cli-components/ProjectGrid/ProjectGrid.js.map +1 -0
- package/dist/cli-components/ProjectGrid/ProjectGrid.tsx +6 -0
- package/dist/cli-components/ProjectGrid/index.d.ts +3 -0
- package/dist/cli-components/ProjectGrid/index.d.ts.map +1 -0
- package/dist/cli-components/ProjectGrid/index.js +2 -0
- package/dist/cli-components/ProjectGrid/index.js.map +1 -0
- package/dist/cli-components/ProjectGrid/index.ts +2 -0
- package/dist/cli-components/ProjectList/ProjectList.d.ts +5 -0
- package/dist/cli-components/ProjectList/ProjectList.d.ts.map +1 -0
- package/dist/cli-components/ProjectList/ProjectList.js +8 -0
- package/dist/cli-components/ProjectList/ProjectList.js.map +1 -0
- package/dist/cli-components/ProjectList/ProjectList.tsx +6 -0
- package/dist/cli-components/ProjectList/index.d.ts +3 -0
- package/dist/cli-components/ProjectList/index.d.ts.map +1 -0
- package/dist/cli-components/ProjectList/index.js +2 -0
- package/dist/cli-components/ProjectList/index.js.map +1 -0
- package/dist/cli-components/ProjectList/index.ts +2 -0
- package/dist/cli-components/ProjectView/ProjectView.d.ts +17 -0
- package/dist/cli-components/ProjectView/ProjectView.d.ts.map +1 -0
- package/dist/cli-components/ProjectView/ProjectView.js +39 -0
- package/dist/cli-components/ProjectView/ProjectView.js.map +1 -0
- package/dist/cli-components/ProjectView/ProjectView.tsx +117 -0
- package/dist/cli-components/ProjectView/index.d.ts +3 -0
- package/dist/cli-components/ProjectView/index.d.ts.map +1 -0
- package/dist/cli-components/ProjectView/index.js +2 -0
- package/dist/cli-components/ProjectView/index.js.map +1 -0
- package/dist/cli-components/ProjectView/index.ts +2 -0
- package/dist/cli-components/types.d.ts +52 -0
- package/dist/cli-components/types.d.ts.map +1 -0
- package/dist/cli-components/types.js +2 -0
- package/dist/cli-components/types.js.map +1 -0
- package/dist/cli-components/types.ts +58 -0
- package/dist/cli-types.d.ts +52 -0
- package/dist/cli-types.d.ts.map +1 -0
- package/dist/cli-types.js +2 -0
- package/dist/cli-types.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +21 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/add.d.ts +2 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +166 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +231 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/components/FeaturedProject.d.ts +7 -0
- package/dist/components/FeaturedProject.d.ts.map +1 -0
- package/dist/components/FeaturedProject.js +10 -0
- package/dist/components/FeaturedProject.js.map +1 -0
- package/dist/components/FeaturedProject.test.tsx +204 -0
- package/dist/components/FeaturedProject.tsx +26 -0
- package/dist/components/ProjectCard/ProjectCard.d.ts +26 -0
- package/dist/components/ProjectCard/ProjectCard.d.ts.map +1 -0
- package/dist/components/ProjectCard/ProjectCard.js +39 -0
- package/dist/components/ProjectCard/ProjectCard.js.map +1 -0
- package/dist/components/ProjectCard/ProjectCard.test.tsx +281 -0
- package/dist/components/ProjectCard/ProjectCard.tsx +98 -0
- package/dist/components/ProjectCard/index.d.ts +2 -0
- package/dist/components/ProjectCard/index.d.ts.map +1 -0
- package/dist/components/ProjectCard/index.js +2 -0
- package/dist/components/ProjectCard/index.js.map +1 -0
- package/dist/components/ProjectCard/index.ts +1 -0
- package/dist/components/ProjectGrid/ProjectGrid.d.ts +5 -0
- package/dist/components/ProjectGrid/ProjectGrid.d.ts.map +1 -0
- package/dist/components/ProjectGrid/ProjectGrid.js +8 -0
- package/dist/components/ProjectGrid/ProjectGrid.js.map +1 -0
- package/dist/components/ProjectGrid/ProjectGrid.test.tsx +69 -0
- package/dist/components/ProjectGrid/ProjectGrid.tsx +6 -0
- package/dist/components/ProjectGrid/index.d.ts +2 -0
- package/dist/components/ProjectGrid/index.d.ts.map +1 -0
- package/dist/components/ProjectGrid/index.js +2 -0
- package/dist/components/ProjectGrid/index.js.map +1 -0
- package/dist/components/ProjectGrid/index.ts +1 -0
- package/dist/components/ProjectList/ProjectList.d.ts +5 -0
- package/dist/components/ProjectList/ProjectList.d.ts.map +1 -0
- package/dist/components/ProjectList/ProjectList.js +8 -0
- package/dist/components/ProjectList/ProjectList.js.map +1 -0
- package/dist/components/ProjectList/ProjectList.test.tsx +69 -0
- package/dist/components/ProjectList/ProjectList.tsx +6 -0
- package/dist/components/ProjectList/index.d.ts +2 -0
- package/dist/components/ProjectList/index.d.ts.map +1 -0
- package/dist/components/ProjectList/index.js +2 -0
- package/dist/components/ProjectList/index.js.map +1 -0
- package/dist/components/ProjectList/index.ts +1 -0
- package/dist/components/ProjectView/ProjectView.d.ts +20 -0
- package/dist/components/ProjectView/ProjectView.d.ts.map +1 -0
- package/dist/components/ProjectView/ProjectView.js +64 -0
- package/dist/components/ProjectView/ProjectView.js.map +1 -0
- package/dist/components/ProjectView/ProjectView.test.tsx +341 -0
- package/dist/components/ProjectView/ProjectView.tsx +168 -0
- package/dist/components/ProjectView/index.d.ts +2 -0
- package/dist/components/ProjectView/index.d.ts.map +1 -0
- package/dist/components/ProjectView/index.js +2 -0
- package/dist/components/ProjectView/index.js.map +1 -0
- package/dist/components/ProjectView/index.ts +1 -0
- package/dist/components/index.d.ts +5 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +5 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.ts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/defineProjects.d.ts +3 -0
- package/dist/lib/defineProjects.d.ts.map +1 -0
- package/dist/lib/defineProjects.js +4 -0
- package/dist/lib/defineProjects.js.map +1 -0
- package/dist/lib/filterByFeatured.d.ts +3 -0
- package/dist/lib/filterByFeatured.d.ts.map +1 -0
- package/dist/lib/filterByFeatured.js +10 -0
- package/dist/lib/filterByFeatured.js.map +1 -0
- package/dist/lib/filterByStatus.d.ts +3 -0
- package/dist/lib/filterByStatus.d.ts.map +1 -0
- package/dist/lib/filterByStatus.js +13 -0
- package/dist/lib/filterByStatus.js.map +1 -0
- package/dist/lib/filterByType.d.ts +3 -0
- package/dist/lib/filterByType.d.ts.map +1 -0
- package/dist/lib/filterByType.js +10 -0
- package/dist/lib/filterByType.js.map +1 -0
- package/dist/lib/github.d.ts +24 -0
- package/dist/lib/github.d.ts.map +1 -0
- package/dist/lib/github.js +107 -0
- package/dist/lib/github.js.map +1 -0
- package/dist/lib/index.d.ts +16 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +12 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/normalise.d.ts +4 -0
- package/dist/lib/normalise.d.ts.map +1 -0
- package/dist/lib/normalise.js +221 -0
- package/dist/lib/normalise.js.map +1 -0
- package/dist/lib/npm.d.ts +7 -0
- package/dist/lib/npm.d.ts.map +1 -0
- package/dist/lib/npm.js +28 -0
- package/dist/lib/npm.js.map +1 -0
- package/dist/lib/product-hunt.d.ts +12 -0
- package/dist/lib/product-hunt.d.ts.map +1 -0
- package/dist/lib/product-hunt.js +40 -0
- package/dist/lib/product-hunt.js.map +1 -0
- package/dist/lib/sortByDate.d.ts +4 -0
- package/dist/lib/sortByDate.d.ts.map +1 -0
- package/dist/lib/sortByDate.js +21 -0
- package/dist/lib/sortByDate.js.map +1 -0
- package/dist/lib/sortByName.d.ts +4 -0
- package/dist/lib/sortByName.d.ts.map +1 -0
- package/dist/lib/sortByName.js +21 -0
- package/dist/lib/sortByName.js.map +1 -0
- package/dist/lib/sortByStars.d.ts +4 -0
- package/dist/lib/sortByStars.d.ts.map +1 -0
- package/dist/lib/sortByStars.js +21 -0
- package/dist/lib/sortByStars.js.map +1 -0
- package/dist/test-setup.d.ts +2 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +2 -0
- package/dist/test-setup.js.map +1 -0
- package/dist/types/index.d.ts +121 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/eslint.config.js +33 -0
- package/package.json +47 -0
- package/playwright-report/index.html +85 -0
- package/src/__tests__/benchmarks/ProjectCard.bench.tsx +93 -0
- package/src/__tests__/benchmarks/ProjectView.bench.tsx +133 -0
- package/src/__tests__/benchmarks/github.bench.ts +53 -0
- package/src/__tests__/benchmarks/npm.bench.ts +62 -0
- package/src/__tests__/benchmarks/product-hunt.bench.ts +58 -0
- package/src/__tests__/benchmarks/utilities.bench.ts +131 -0
- package/src/cli-components/FeaturedProject/FeaturedProject.tsx +54 -0
- package/src/cli-components/FeaturedProject/index.ts +2 -0
- package/src/cli-components/ProjectCard/ProjectCard.tsx +90 -0
- package/src/cli-components/ProjectCard/index.ts +2 -0
- package/src/cli-components/ProjectGrid/ProjectGrid.tsx +6 -0
- package/src/cli-components/ProjectGrid/index.ts +2 -0
- package/src/cli-components/ProjectList/ProjectList.tsx +6 -0
- package/src/cli-components/ProjectList/index.ts +2 -0
- package/src/cli-components/ProjectView/ProjectView.tsx +117 -0
- package/src/cli-components/ProjectView/index.ts +2 -0
- package/src/cli-components/types.ts +58 -0
- package/src/cli-types.ts +58 -0
- package/src/cli.ts +26 -0
- package/src/commands/add.ts +191 -0
- package/src/commands/init.ts +260 -0
- package/src/components/FeaturedProject.test.tsx +204 -0
- package/src/components/FeaturedProject.tsx +26 -0
- package/src/components/ProjectCard/ProjectCard.test.tsx +281 -0
- package/src/components/ProjectCard/ProjectCard.tsx +98 -0
- package/src/components/ProjectCard/index.ts +1 -0
- package/src/components/ProjectGrid/ProjectGrid.test.tsx +69 -0
- package/src/components/ProjectGrid/ProjectGrid.tsx +6 -0
- package/src/components/ProjectGrid/index.ts +1 -0
- package/src/components/ProjectList/ProjectList.test.tsx +69 -0
- package/src/components/ProjectList/ProjectList.tsx +6 -0
- package/src/components/ProjectList/index.ts +1 -0
- package/src/components/ProjectView/ProjectView.test.tsx +341 -0
- package/src/components/ProjectView/ProjectView.tsx +168 -0
- package/src/components/ProjectView/index.ts +1 -0
- package/src/components/index.ts +4 -0
- package/src/index.ts +3 -0
- package/src/lib/__tests__/defineProjects.test.ts +112 -0
- package/src/lib/__tests__/filterByFeatured.test.ts +146 -0
- package/src/lib/__tests__/filterByStatus.test.ts +193 -0
- package/src/lib/__tests__/filterByType.test.ts +182 -0
- package/src/lib/__tests__/github.test.ts +566 -0
- package/src/lib/__tests__/hybrid-config.test.ts +420 -0
- package/src/lib/__tests__/normalise.test.ts +500 -0
- package/src/lib/__tests__/npm-config.test.ts +100 -0
- package/src/lib/__tests__/npm.test.ts +350 -0
- package/src/lib/__tests__/product-hunt-config.test.ts +104 -0
- package/src/lib/__tests__/product-hunt.test.ts +140 -0
- package/src/lib/__tests__/sortByDate.test.ts +222 -0
- package/src/lib/__tests__/sortByName.test.ts +249 -0
- package/src/lib/__tests__/sortByStars.test.ts +206 -0
- package/src/lib/defineProjects.ts +5 -0
- package/src/lib/filterByFeatured.ts +16 -0
- package/src/lib/filterByStatus.ts +20 -0
- package/src/lib/filterByType.ts +16 -0
- package/src/lib/github.ts +144 -0
- package/src/lib/index.ts +15 -0
- package/src/lib/normalise.ts +261 -0
- package/src/lib/npm.ts +38 -0
- package/src/lib/product-hunt.ts +57 -0
- package/src/lib/sortByDate.ts +30 -0
- package/src/lib/sortByName.ts +29 -0
- package/src/lib/sortByStars.ts +29 -0
- package/src/test-setup.ts +1 -0
- package/src/types/index.ts +144 -0
- package/test-results/.last-run.json +62 -0
- package/tsconfig.json +29 -0
- 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,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 }
|