@grove-dev/astro 0.2.20 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +9 -75
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/display.d.ts +1 -26
- package/dist/lib/display.d.ts.map +1 -1
- package/dist/lib/display.js +1 -103
- package/dist/lib/display.js.map +1 -1
- package/dist/lib/format.d.ts +1 -28
- package/dist/lib/format.d.ts.map +1 -1
- package/dist/lib/format.js +1 -71
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/lenses.d.ts +1 -57
- package/dist/lib/lenses.d.ts.map +1 -1
- package/dist/lib/lenses.js +1 -198
- package/dist/lib/lenses.js.map +1 -1
- package/dist/lib/repo.d.ts +1 -19
- package/dist/lib/repo.d.ts.map +1 -1
- package/dist/lib/repo.js +1 -30
- package/dist/lib/repo.js.map +1 -1
- package/dist/lib/scores.d.ts +1 -36
- package/dist/lib/scores.d.ts.map +1 -1
- package/dist/lib/scores.js +1 -75
- package/dist/lib/scores.js.map +1 -1
- package/dist/lib/search.d.ts +1 -113
- package/dist/lib/search.d.ts.map +1 -1
- package/dist/lib/search.js +1 -379
- package/dist/lib/search.js.map +1 -1
- package/dist/lib/taxonomy-counts.d.ts +1 -10
- package/dist/lib/taxonomy-counts.d.ts.map +1 -1
- package/dist/lib/taxonomy-counts.js +1 -21
- package/dist/lib/taxonomy-counts.js.map +1 -1
- package/package.json +22 -10
- package/src/components/ContributorsGrid.astro +3 -3
- package/src/components/CurationGrid.astro +2 -2
- package/src/components/DirectoryIndexClient.astro +240 -0
- package/src/components/FilterGroupMenu.astro +8 -4
- package/src/components/FinalCta.astro +41 -0
- package/src/components/Hero.astro +15 -18
- package/src/components/Icon.astro +99 -27
- package/src/components/RefinePanel.astro +7 -7
- package/src/components/SmartLensTabs.astro +5 -4
- package/src/components/StackGrid.astro +1 -1
- package/src/components/SubmissionClient.astro +133 -0
- package/src/index.ts +11 -1
- package/src/layouts/BaseLayout.astro +19 -1
- package/src/layouts/Footer.astro +25 -56
- package/src/layouts/Header.astro +33 -15
- package/src/layouts/ThemeToggle.astro +7 -3
- package/src/lib/display.ts +1 -111
- package/src/lib/format.ts +1 -64
- package/src/lib/lenses.ts +1 -220
- package/src/lib/repo.ts +1 -32
- package/src/lib/scores.ts +1 -90
- package/src/lib/search.ts +1 -418
- package/src/lib/taxonomy-counts.ts +1 -26
- package/{templates/default/src/data/records.ts → src/server/directory.ts} +90 -8
- package/src/server/index.ts +2 -0
- package/src/server/models.ts +432 -0
- package/dist/theme.test.d.ts +0 -2
- package/dist/theme.test.d.ts.map +0 -1
- package/dist/theme.test.js +0 -55
- package/dist/theme.test.js.map +0 -1
- package/src/theme.test.ts +0 -67
- package/templates/default/.github/ISSUE_TEMPLATE/bug_report.md +0 -33
- package/templates/default/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- package/templates/default/.github/ISSUE_TEMPLATE/record_submission.md +0 -48
- package/templates/default/.github/pull_request_template.md +0 -27
- package/templates/default/.github/workflows/build.yml +0 -55
- package/templates/default/.github/workflows/cleanup-stale-records.yml +0 -48
- package/templates/default/.github/workflows/daily-refresh.yml +0 -53
- package/templates/default/.github/workflows/sync-contributors.yml +0 -53
- package/templates/default/.github/workflows/sync-github-metadata.yml +0 -58
- package/templates/default/.github/workflows/validate-data.yml +0 -35
- package/templates/default/astro.config.mjs +0 -25
- package/templates/default/content/pages/about.md +0 -66
- package/templates/default/content/pages/methodology.md +0 -103
- package/templates/default/data/records/.gitkeep +0 -0
- package/templates/default/data/records/coolify.yml +0 -46
- package/templates/default/data/records/gitea.yml +0 -46
- package/templates/default/data/records/hoppscotch.yml +0 -47
- package/templates/default/data/records/inventree.yml +0 -47
- package/templates/default/data/records/label-studio.yml +0 -46
- package/templates/default/data/records/logseq.yml +0 -48
- package/templates/default/data/records/private-gpt.yml +0 -47
- package/templates/default/data/records/wakapi.yml +0 -44
- package/templates/default/data/taxonomy/categories.yml +0 -47
- package/templates/default/data/taxonomy/distribution-channels.yml +0 -43
- package/templates/default/data/taxonomy/platforms.yml +0 -27
- package/templates/default/data/taxonomy/stacks.yml +0 -41
- package/templates/default/grove.config.ts +0 -36
- package/templates/default/package.json +0 -38
- package/templates/default/public/icons/platforms/android.svg +0 -1
- package/templates/default/public/icons/platforms/apple-dark.svg +0 -1
- package/templates/default/public/icons/platforms/apple-light.svg +0 -1
- package/templates/default/public/icons/platforms/chrome.svg +0 -1
- package/templates/default/public/icons/platforms/chromeos.svg +0 -5
- package/templates/default/public/icons/platforms/desktop.svg +0 -4
- package/templates/default/public/icons/platforms/embedded.svg +0 -5
- package/templates/default/public/icons/platforms/ios.svg +0 -1
- package/templates/default/public/icons/platforms/linux.svg +0 -1
- package/templates/default/public/icons/platforms/macos.svg +0 -1
- package/templates/default/public/icons/platforms/ubuntu.svg +0 -1
- package/templates/default/public/icons/platforms/web.svg +0 -5
- package/templates/default/public/icons/platforms/windows.svg +0 -6
- package/templates/default/public/icons/stacks/android.svg +0 -1
- package/templates/default/public/icons/stacks/apple-dark.svg +0 -1
- package/templates/default/public/icons/stacks/apple-light.svg +0 -1
- package/templates/default/public/icons/stacks/bun.svg +0 -1
- package/templates/default/public/icons/stacks/capacitor.svg +0 -14
- package/templates/default/public/icons/stacks/clojure.svg +0 -1
- package/templates/default/public/icons/stacks/clojurescript.svg +0 -1
- package/templates/default/public/icons/stacks/dart.svg +0 -1
- package/templates/default/public/icons/stacks/deno.svg +0 -1
- package/templates/default/public/icons/stacks/django.svg +0 -1
- package/templates/default/public/icons/stacks/docker.svg +0 -1
- package/templates/default/public/icons/stacks/firebase.svg +0 -1
- package/templates/default/public/icons/stacks/flutter.svg +0 -1
- package/templates/default/public/icons/stacks/go.svg +0 -1
- package/templates/default/public/icons/stacks/graphql.svg +0 -1
- package/templates/default/public/icons/stacks/ionic.svg +0 -4
- package/templates/default/public/icons/stacks/java.svg +0 -1
- package/templates/default/public/icons/stacks/javascript.svg +0 -1
- package/templates/default/public/icons/stacks/kotlin.svg +0 -1
- package/templates/default/public/icons/stacks/llm.svg +0 -1
- package/templates/default/public/icons/stacks/mongodb.svg +0 -1
- package/templates/default/public/icons/stacks/node.js.svg +0 -1
- package/templates/default/public/icons/stacks/nodejs.svg +0 -1
- package/templates/default/public/icons/stacks/python.svg +0 -1
- package/templates/default/public/icons/stacks/rag.svg +0 -7
- package/templates/default/public/icons/stacks/react-native.svg +0 -1
- package/templates/default/public/icons/stacks/react.svg +0 -1
- package/templates/default/public/icons/stacks/rust.svg +0 -1
- package/templates/default/public/icons/stacks/solidity.svg +0 -1
- package/templates/default/public/icons/stacks/sveltekit.svg +0 -1
- package/templates/default/public/icons/stacks/swift.svg +0 -1
- package/templates/default/public/icons/stacks/tensorflow.svg +0 -1
- package/templates/default/public/icons/stacks/typescript.svg +0 -1
- package/templates/default/public/icons/stacks/vue.svg +0 -1
- package/templates/default/public/llms-full.txt +0 -206
- package/templates/default/public/llms.txt +0 -112
- package/templates/default/public/og-image.svg +0 -48
- package/templates/default/public/robots.txt +0 -53
- package/templates/default/public/sitemap.xml +0 -63
- package/templates/default/scripts/build-llms.mjs +0 -123
- package/templates/default/scripts/build-records-json.mjs +0 -27
- package/templates/default/scripts/build-sitemap.mjs +0 -20
- package/templates/default/scripts/cleanup-stale-records.mjs +0 -20
- package/templates/default/scripts/enrich-github-metadata.mjs +0 -99
- package/templates/default/scripts/fetch-icons.mjs +0 -137
- package/templates/default/scripts/migrate-legacy-to-schema-v1.mjs +0 -86
- package/templates/default/scripts/parse-legacy-readme.mjs +0 -59
- package/templates/default/scripts/refresh-records-activity.mjs +0 -24
- package/templates/default/scripts/repair-license-format.mjs +0 -72
- package/templates/default/scripts/report-cleanup-candidates.mjs +0 -21
- package/templates/default/scripts/seed-from-github.mjs +0 -62
- package/templates/default/scripts/sync-contributors.mjs +0 -145
- package/templates/default/scripts/sync-github-metadata.mjs +0 -28
- package/templates/default/scripts/validate-records.mjs +0 -27
- package/templates/default/src/pages/404.astro +0 -64
- package/templates/default/src/pages/[slug]/[recordSlug].astro +0 -689
- package/templates/default/src/pages/[slug]/index.astro +0 -278
- package/templates/default/src/pages/about.astro +0 -182
- package/templates/default/src/pages/apps/[recordSlug].astro +0 -33
- package/templates/default/src/pages/contributors.astro +0 -175
- package/templates/default/src/pages/index.astro +0 -225
- package/templates/default/src/pages/sitemap.xml.ts +0 -89
- package/templates/default/src/pages/submit.astro +0 -281
- package/templates/default/src/styles/global.css +0 -33
- package/templates/default/wrangler.jsonc +0 -28
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page view-models for Grove's Astro adapter.
|
|
3
|
+
*
|
|
4
|
+
* These functions contain directory decisions and generated-data access,
|
|
5
|
+
* while consumer-owned Astro pages keep control of markup and composition.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { resolve } from "node:path";
|
|
9
|
+
import type { IndexFilters, IndexRecord, ProjectRecord } from "@grove-dev/core";
|
|
10
|
+
import {
|
|
11
|
+
activeFilterChips,
|
|
12
|
+
applySort,
|
|
13
|
+
buildFacets,
|
|
14
|
+
effectivePage,
|
|
15
|
+
effectiveSort,
|
|
16
|
+
filterRecords,
|
|
17
|
+
filtersFromSearchParams,
|
|
18
|
+
formatRelative,
|
|
19
|
+
formatStars,
|
|
20
|
+
getOwnerAndRepoFromRepoUrl,
|
|
21
|
+
getOwnerAvatarUrl,
|
|
22
|
+
statusDisplay,
|
|
23
|
+
totalPages,
|
|
24
|
+
} from "@grove-dev/core";
|
|
25
|
+
import {
|
|
26
|
+
entityBySlug,
|
|
27
|
+
fullItems,
|
|
28
|
+
fullProjects,
|
|
29
|
+
getContentHtml,
|
|
30
|
+
itemLabel,
|
|
31
|
+
itemLabelPlural,
|
|
32
|
+
items,
|
|
33
|
+
projectBySlug,
|
|
34
|
+
resourceBySlug,
|
|
35
|
+
taxonomyLabel,
|
|
36
|
+
} from "./directory.js";
|
|
37
|
+
|
|
38
|
+
export interface DirectorySiteConfig {
|
|
39
|
+
name: string;
|
|
40
|
+
tagline?: string;
|
|
41
|
+
description?: string;
|
|
42
|
+
repoUrl?: string;
|
|
43
|
+
nav?: Array<{ label: string; href: string }>;
|
|
44
|
+
facets?: string[];
|
|
45
|
+
footer?: {
|
|
46
|
+
columns?: Array<{
|
|
47
|
+
heading: string;
|
|
48
|
+
items: Array<{ label: string; href: string; external?: boolean }>;
|
|
49
|
+
}>;
|
|
50
|
+
copyright?: string;
|
|
51
|
+
license?: string;
|
|
52
|
+
};
|
|
53
|
+
submission?: {
|
|
54
|
+
eyebrow?: string;
|
|
55
|
+
title?: string;
|
|
56
|
+
description?: string;
|
|
57
|
+
good?: string[];
|
|
58
|
+
avoid?: string[];
|
|
59
|
+
};
|
|
60
|
+
blueprintConfig?: {
|
|
61
|
+
routeSlug?: string;
|
|
62
|
+
labelSingular?: string;
|
|
63
|
+
labelPlural?: string;
|
|
64
|
+
};
|
|
65
|
+
taxonomy?: {
|
|
66
|
+
categories?: Array<{ id: string; name: string }>;
|
|
67
|
+
stacks?: Array<{ id: string; name: string }>;
|
|
68
|
+
platforms?: Array<{ id: string; name: string }>;
|
|
69
|
+
};
|
|
70
|
+
stats?: {
|
|
71
|
+
originalRepo?: string;
|
|
72
|
+
originalStars?: number;
|
|
73
|
+
originalForks?: number;
|
|
74
|
+
originalContributors?: number;
|
|
75
|
+
totalApps?: number;
|
|
76
|
+
totalCategories?: number;
|
|
77
|
+
totalStacks?: number;
|
|
78
|
+
totalOwners?: number;
|
|
79
|
+
totalStars?: number;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface DirectoryContributor {
|
|
84
|
+
username: string;
|
|
85
|
+
name?: string;
|
|
86
|
+
avatarUrl?: string;
|
|
87
|
+
profileUrl?: string;
|
|
88
|
+
contributions?: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function loadDirectoryContributors(root = process.cwd()): DirectoryContributor[] {
|
|
92
|
+
const path = resolve(root, "data", "generated", "contributors.json");
|
|
93
|
+
if (!existsSync(path)) return [];
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(readFileSync(path, "utf8")) as {
|
|
96
|
+
contributors?: DirectoryContributor[];
|
|
97
|
+
};
|
|
98
|
+
return parsed.contributors ?? [];
|
|
99
|
+
} catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function getHomePageModel(site: DirectorySiteConfig) {
|
|
105
|
+
const slug = site.blueprintConfig?.routeSlug ?? "projects";
|
|
106
|
+
const singular = site.blueprintConfig?.labelSingular ?? itemLabel();
|
|
107
|
+
const plural = site.blueprintConfig?.labelPlural ?? itemLabelPlural();
|
|
108
|
+
const projects = items.filter((record) => record.kind === "project");
|
|
109
|
+
const hot = filterRecords(projects, { labels: ["hot"] }).slice(0, 6);
|
|
110
|
+
const recentlyAdded = applySort(projects, "recently-added").slice(0, 6);
|
|
111
|
+
const established = filterRecords(projects, { labels: ["mature"] }).slice(0, 6);
|
|
112
|
+
|
|
113
|
+
const stackCounts = new Map<string, number>();
|
|
114
|
+
const categoryCounts = new Map<string, number>();
|
|
115
|
+
for (const record of fullItems) {
|
|
116
|
+
if (record.category) categoryCounts.set(record.category, (categoryCounts.get(record.category) ?? 0) + 1);
|
|
117
|
+
if (record.kind === "project" && record.stack) {
|
|
118
|
+
stackCounts.set(record.stack, (stackCounts.get(record.stack) ?? 0) + 1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const stacks = [...stackCounts]
|
|
122
|
+
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
|
|
123
|
+
.slice(0, 12)
|
|
124
|
+
.map(([id, count]) => ({ name: taxonomyLabel("stacks", id), slug: id, count }));
|
|
125
|
+
const categories = [...categoryCounts]
|
|
126
|
+
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
|
|
127
|
+
.map(([id, count]) => ({ name: taxonomyLabel("categories", id), slug: id, count }));
|
|
128
|
+
const contributors = loadDirectoryContributors();
|
|
129
|
+
const description = site.description ??
|
|
130
|
+
`A searchable directory of real ${plural} — organized by stack, category, platform, license, activity, and maturity.`;
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
slug,
|
|
134
|
+
itemSingular: singular,
|
|
135
|
+
itemPlural: plural,
|
|
136
|
+
hot,
|
|
137
|
+
recentlyAdded,
|
|
138
|
+
established,
|
|
139
|
+
stacks,
|
|
140
|
+
categories,
|
|
141
|
+
contributors,
|
|
142
|
+
title: `${site.name} — ${site.tagline ?? ""}`.trim(),
|
|
143
|
+
description,
|
|
144
|
+
stats: {
|
|
145
|
+
originalRepo: site.stats?.originalRepo ?? "",
|
|
146
|
+
apps: site.stats?.totalApps ?? 0,
|
|
147
|
+
categories: site.stats?.totalCategories ?? 0,
|
|
148
|
+
stacks: site.stats?.totalStacks ?? 0,
|
|
149
|
+
owners: site.stats?.totalOwners ?? 0,
|
|
150
|
+
stars: site.stats?.totalStars ?? 0,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const FACET_ALIASES: Record<string, "stacks" | "platforms" | "categories" | "tags" | "licenses"> = {
|
|
156
|
+
stack: "stacks",
|
|
157
|
+
stacks: "stacks",
|
|
158
|
+
platform: "platforms",
|
|
159
|
+
platforms: "platforms",
|
|
160
|
+
category: "categories",
|
|
161
|
+
categories: "categories",
|
|
162
|
+
tag: "tags",
|
|
163
|
+
tags: "tags",
|
|
164
|
+
license: "licenses",
|
|
165
|
+
licenses: "licenses",
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
function configuredFacets(site?: DirectorySiteConfig) {
|
|
169
|
+
return new Set((site?.facets ?? ["category", "tags"])
|
|
170
|
+
.map((facet) => FACET_ALIASES[facet])
|
|
171
|
+
.filter((facet): facet is NonNullable<typeof facet> => Boolean(facet)));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function getDirectoryIndexModel(searchParams: URLSearchParams, site?: DirectorySiteConfig) {
|
|
175
|
+
const filters = filtersFromSearchParams(searchParams);
|
|
176
|
+
const rawFacets = buildFacets(items);
|
|
177
|
+
const enabled = configuredFacets(site);
|
|
178
|
+
const facets = {
|
|
179
|
+
stacks: enabled.has("stacks") ? rawFacets.stacks.map((option) => ({ ...option, label: taxonomyLabel("stacks", option.value) })) : [],
|
|
180
|
+
platforms: enabled.has("platforms") ? rawFacets.platforms.map((option) => ({ ...option, label: taxonomyLabel("platforms", option.value) })) : [],
|
|
181
|
+
categories: enabled.has("categories") ? rawFacets.categories.map((option) => ({ ...option, label: taxonomyLabel("categories", option.value) })) : [],
|
|
182
|
+
tags: enabled.has("tags") ? rawFacets.tags : [],
|
|
183
|
+
licenses: enabled.has("licenses") ? rawFacets.licenses : [],
|
|
184
|
+
};
|
|
185
|
+
const sort = effectiveSort(filters);
|
|
186
|
+
const sorted = applySort(filterRecords(items, filters), sort);
|
|
187
|
+
const pageCount = totalPages(sorted.length);
|
|
188
|
+
const page = Math.min(effectivePage(filters), pageCount);
|
|
189
|
+
return {
|
|
190
|
+
items,
|
|
191
|
+
total: items.length,
|
|
192
|
+
filters,
|
|
193
|
+
facets,
|
|
194
|
+
sort,
|
|
195
|
+
sorted,
|
|
196
|
+
pages: pageCount,
|
|
197
|
+
page,
|
|
198
|
+
chips: activeFilterChips(filters),
|
|
199
|
+
clientItemsJson: JSON.stringify(items).replace(/</g, "\\u003c"),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function getContributorsPageModel(site: DirectorySiteConfig) {
|
|
204
|
+
const contributors = loadDirectoryContributors();
|
|
205
|
+
const sorted = [...contributors].sort((a, b) =>
|
|
206
|
+
(b.contributions ?? 0) - (a.contributions ?? 0) || a.username.localeCompare(b.username)
|
|
207
|
+
);
|
|
208
|
+
const repo = site.repoUrl?.replace(/\/$/, "");
|
|
209
|
+
return {
|
|
210
|
+
contributors: sorted,
|
|
211
|
+
total: sorted.length,
|
|
212
|
+
title: `Community — ${site.name}`,
|
|
213
|
+
description: `The people who have contributed to ${site.name} through code, curation, and review.`,
|
|
214
|
+
contributorsGraphHref: repo ? `${repo}/graphs/contributors` : null,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function getSubmissionPageModel(site: DirectorySiteConfig) {
|
|
219
|
+
const singular = site.blueprintConfig?.labelSingular ?? itemLabel();
|
|
220
|
+
const enabled = configuredFacets(site);
|
|
221
|
+
const values = (key: "category" | "stack") =>
|
|
222
|
+
[...new Set(fullProjects.map((record) => String(record[key] ?? "")).filter(Boolean))]
|
|
223
|
+
.sort((a, b) => a.localeCompare(b));
|
|
224
|
+
const categories = (site.taxonomy?.categories?.length
|
|
225
|
+
? site.taxonomy.categories.map(({ id }) => id)
|
|
226
|
+
: values("category"))
|
|
227
|
+
.map((id) => ({ id, label: taxonomyLabel("categories", id) }));
|
|
228
|
+
const stacks = (site.taxonomy?.stacks?.length
|
|
229
|
+
? site.taxonomy.stacks.map(({ id }) => id)
|
|
230
|
+
: values("stack"))
|
|
231
|
+
.map((id) => ({ id, label: taxonomyLabel("stacks", id) }));
|
|
232
|
+
const existingFullNames = fullProjects.map((record) => {
|
|
233
|
+
const url = String(record.repoUrl ?? record.links?.github ?? "");
|
|
234
|
+
const match = url.match(/github\.com\/([^/]+)\/([^/?#]+)/i);
|
|
235
|
+
return match ? `${match[1]}/${match[2].replace(/\.git$/, "")}`.toLowerCase() : null;
|
|
236
|
+
}).filter((value): value is string => Boolean(value));
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
singular,
|
|
240
|
+
title: `Submit ${singular} - ${site.name}`,
|
|
241
|
+
description: `Submit a new ${singular} to ${site.name}.`,
|
|
242
|
+
repoUrl: site.repoUrl ?? "https://github.com/tortuvshin/grove",
|
|
243
|
+
copy: site.submission ?? {},
|
|
244
|
+
fields: {
|
|
245
|
+
category: enabled.has("categories"),
|
|
246
|
+
stack: enabled.has("stacks"),
|
|
247
|
+
platforms: enabled.has("platforms"),
|
|
248
|
+
tags: enabled.has("tags"),
|
|
249
|
+
},
|
|
250
|
+
categories,
|
|
251
|
+
stacks,
|
|
252
|
+
existingSlugs: fullProjects.map((record) => record.slug).filter(Boolean),
|
|
253
|
+
existingFullNames,
|
|
254
|
+
platformOptions: site.taxonomy?.platforms?.map(({ id, name }) => ({ id, label: name })) ??
|
|
255
|
+
["ios", "android", "web", "macos", "windows", "linux"].map((id) => ({ id, label: taxonomyLabel("platforms", id) })),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
type MonthlyCommit = number | { month: string; commits: number };
|
|
260
|
+
type ProjectExtras = {
|
|
261
|
+
github?: {
|
|
262
|
+
activity?: { monthlyCommits?: MonthlyCommit[] };
|
|
263
|
+
files?: Record<string, boolean>;
|
|
264
|
+
languages?: Record<string, number>;
|
|
265
|
+
};
|
|
266
|
+
health?: { status?: string; tier?: string };
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export function getRecordDetailModel(
|
|
270
|
+
recordSlug: string,
|
|
271
|
+
site: DirectorySiteConfig,
|
|
272
|
+
routeSlug = site.blueprintConfig?.routeSlug ?? "projects",
|
|
273
|
+
) {
|
|
274
|
+
const project = projectBySlug(recordSlug);
|
|
275
|
+
const resource = resourceBySlug(recordSlug);
|
|
276
|
+
const entity = entityBySlug(recordSlug);
|
|
277
|
+
const record = project ?? resource ?? entity;
|
|
278
|
+
if (!record) return null;
|
|
279
|
+
|
|
280
|
+
const isProject = record.kind === "project";
|
|
281
|
+
const proj: ProjectRecord | null = project ?? null;
|
|
282
|
+
const name = record.kind === "resource" ? record.title : record.name;
|
|
283
|
+
const description = record.description ?? `${name} on ${site.name}`;
|
|
284
|
+
const repoUrl = proj?.repoUrl ?? record.links?.github ?? "";
|
|
285
|
+
const homepageUrl = record.links?.website ?? "";
|
|
286
|
+
const stacks = proj?.stacks ?? (proj?.stack ? [proj.stack] : []);
|
|
287
|
+
const platforms = proj?.platforms ?? [];
|
|
288
|
+
const github = proj?.github?.repository;
|
|
289
|
+
const stars = github?.stargazers_count ?? 0;
|
|
290
|
+
const forks = github?.forks_count ?? 0;
|
|
291
|
+
const language = github?.language ?? null;
|
|
292
|
+
const licenseSpdx = github?.license?.spdx_id ?? null;
|
|
293
|
+
const pushedAt = github?.pushed_at ?? null;
|
|
294
|
+
const ownerRepo = repoUrl ? getOwnerAndRepoFromRepoUrl(repoUrl) : null;
|
|
295
|
+
const avatarSrc = proj?.logoUrl ?? (ownerRepo ? getOwnerAvatarUrl(ownerRepo.owner, 80) : null);
|
|
296
|
+
const rawScores = proj?.scores;
|
|
297
|
+
const scores = rawScores && Object.values(rawScores).some((value) => typeof value === "number")
|
|
298
|
+
? rawScores
|
|
299
|
+
: null;
|
|
300
|
+
const healthStatus = proj?.health?.status;
|
|
301
|
+
const extras = record as ProjectExtras;
|
|
302
|
+
const monthlyCommits = (extras.github?.activity?.monthlyCommits ?? []).map(
|
|
303
|
+
(value): { month: string; commits: number } => typeof value === "number"
|
|
304
|
+
? { month: "", commits: value }
|
|
305
|
+
: { month: value.month ?? "", commits: value.commits ?? 0 },
|
|
306
|
+
);
|
|
307
|
+
const contributionLabels: Record<string, string> = {
|
|
308
|
+
readme: "README",
|
|
309
|
+
contributing: "Contributing",
|
|
310
|
+
security: "Security",
|
|
311
|
+
issueTemplates: "Issue templates",
|
|
312
|
+
pullRequestTemplate: "PR template",
|
|
313
|
+
};
|
|
314
|
+
const contributionFiles = extras.github?.files ?? {};
|
|
315
|
+
const contributionSignals = Object.keys(contributionLabels)
|
|
316
|
+
.map((key) => ({ key, label: contributionLabels[key], ok: contributionFiles[key] }))
|
|
317
|
+
.filter((signal) => signal.ok !== undefined);
|
|
318
|
+
const languages = extras.github?.languages
|
|
319
|
+
? Object.entries(extras.github.languages).sort((a, b) => b[1] - a[1]).slice(0, 5)
|
|
320
|
+
: [];
|
|
321
|
+
|
|
322
|
+
let jsonLd: Record<string, unknown>;
|
|
323
|
+
if (isProject && proj) {
|
|
324
|
+
const sameAs = [repoUrl, homepageUrl].filter(Boolean);
|
|
325
|
+
const dateCreated = record.curation?.reviewedAt;
|
|
326
|
+
jsonLd = {
|
|
327
|
+
"@context": "https://schema.org",
|
|
328
|
+
"@type": "SoftwareSourceCode",
|
|
329
|
+
name,
|
|
330
|
+
headline: name,
|
|
331
|
+
description: record.description ?? undefined,
|
|
332
|
+
url: repoUrl || homepageUrl || undefined,
|
|
333
|
+
codeRepository: repoUrl || undefined,
|
|
334
|
+
sameAs: sameAs.length ? sameAs : undefined,
|
|
335
|
+
programmingLanguage: language ?? undefined,
|
|
336
|
+
license: licenseSpdx ?? undefined,
|
|
337
|
+
applicationCategory: record.category || undefined,
|
|
338
|
+
operatingSystem: platforms.length ? platforms.join(", ") : undefined,
|
|
339
|
+
keywords: record.tags?.length ? record.tags.join(", ") : undefined,
|
|
340
|
+
dateCreated: dateCreated ?? undefined,
|
|
341
|
+
dateModified: pushedAt ?? dateCreated ?? undefined,
|
|
342
|
+
interactionStatistic: stars > 0 ? {
|
|
343
|
+
"@type": "InteractionCounter",
|
|
344
|
+
interactionType: { "@type": "LikeAction" },
|
|
345
|
+
userInteractionCount: stars,
|
|
346
|
+
} : undefined,
|
|
347
|
+
author: ownerRepo ? {
|
|
348
|
+
"@type": "Organization",
|
|
349
|
+
name: ownerRepo.owner,
|
|
350
|
+
url: `https://github.com/${ownerRepo.owner}`,
|
|
351
|
+
} : undefined,
|
|
352
|
+
isAccessibleForFree: true,
|
|
353
|
+
};
|
|
354
|
+
} else if (record.kind === "resource") {
|
|
355
|
+
const schemaTypes: Record<string, string> = {
|
|
356
|
+
article: "Article", book: "Book", course: "Course", podcast: "PodcastSeries", video: "VideoObject",
|
|
357
|
+
};
|
|
358
|
+
jsonLd = {
|
|
359
|
+
"@context": "https://schema.org",
|
|
360
|
+
"@type": schemaTypes[record.type] ?? "CreativeWork",
|
|
361
|
+
name,
|
|
362
|
+
headline: name,
|
|
363
|
+
description: record.description || undefined,
|
|
364
|
+
url: homepageUrl || undefined,
|
|
365
|
+
author: record.author ? { "@type": "Person", name: record.author } : undefined,
|
|
366
|
+
datePublished: record.publishedAt || undefined,
|
|
367
|
+
keywords: record.tags?.length ? record.tags.join(", ") : undefined,
|
|
368
|
+
isAccessibleForFree: true,
|
|
369
|
+
};
|
|
370
|
+
} else {
|
|
371
|
+
jsonLd = {
|
|
372
|
+
"@context": "https://schema.org",
|
|
373
|
+
"@type": entity?.type === "person" ? "Person" : "Organization",
|
|
374
|
+
name,
|
|
375
|
+
headline: name,
|
|
376
|
+
description: entity?.description || undefined,
|
|
377
|
+
url: homepageUrl || undefined,
|
|
378
|
+
foundingDate: entity?.founded || undefined,
|
|
379
|
+
location: entity?.location || undefined,
|
|
380
|
+
keywords: entity?.tags?.length ? entity.tags.join(", ") : undefined,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
slug: routeSlug,
|
|
386
|
+
record,
|
|
387
|
+
project: proj,
|
|
388
|
+
entity,
|
|
389
|
+
isProject,
|
|
390
|
+
name,
|
|
391
|
+
title: `${name} — ${site.name}`,
|
|
392
|
+
description,
|
|
393
|
+
itemSingular: site.blueprintConfig?.labelSingular ?? record.kind,
|
|
394
|
+
repoUrl,
|
|
395
|
+
homepageUrl,
|
|
396
|
+
stacks,
|
|
397
|
+
platforms,
|
|
398
|
+
github,
|
|
399
|
+
stars,
|
|
400
|
+
forks,
|
|
401
|
+
language,
|
|
402
|
+
licenseSpdx,
|
|
403
|
+
pushedAt,
|
|
404
|
+
starsLabel: formatStars(stars),
|
|
405
|
+
pushedLabel: formatRelative(pushedAt),
|
|
406
|
+
ownerRepo,
|
|
407
|
+
avatarSrc,
|
|
408
|
+
initials: name.replace(/[^a-z0-9]/gi, "").slice(0, 2).toUpperCase(),
|
|
409
|
+
bestFor: proj?.bestFor ?? [],
|
|
410
|
+
whyListed: proj?.whyListed ?? [],
|
|
411
|
+
caveats: proj?.caveats ?? [],
|
|
412
|
+
distribution: proj?.distribution?.channels ?? [],
|
|
413
|
+
scores,
|
|
414
|
+
curationLabels: record.curation?.labels ?? [],
|
|
415
|
+
tags: record.tags ?? [],
|
|
416
|
+
healthLabel: healthStatus ? statusDisplay(healthStatus) : null,
|
|
417
|
+
contentHtml: isProject ? getContentHtml(recordSlug) : null,
|
|
418
|
+
monthlyCommits,
|
|
419
|
+
maxMonthlyCommits: Math.max(1, ...monthlyCommits.map((item) => item.commits)),
|
|
420
|
+
contributionSignals,
|
|
421
|
+
languages,
|
|
422
|
+
totalLanguageBytes: Math.max(1, languages.reduce((sum, [, bytes]) => sum + bytes, 0)),
|
|
423
|
+
jsonLd,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export type DirectoryIndexModel = ReturnType<typeof getDirectoryIndexModel>;
|
|
428
|
+
export type DirectoryHomeModel = ReturnType<typeof getHomePageModel>;
|
|
429
|
+
export type SubmissionPageModel = ReturnType<typeof getSubmissionPageModel>;
|
|
430
|
+
export type ContributorsPageModel = ReturnType<typeof getContributorsPageModel>;
|
|
431
|
+
export type RecordDetailModel = NonNullable<ReturnType<typeof getRecordDetailModel>>;
|
|
432
|
+
export type { IndexFilters };
|
package/dist/theme.test.d.ts
DELETED
package/dist/theme.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"theme.test.d.ts","sourceRoot":"","sources":["../src/theme.test.ts"],"names":[],"mappings":""}
|
package/dist/theme.test.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
-
const astroTheme = readFileSync(resolve(import.meta.dirname, "styles.css"), "utf8");
|
|
5
|
-
const scaffoldTheme = readFileSync(resolve(import.meta.dirname, "../templates/default/src/styles/global.css"), "utf8");
|
|
6
|
-
const tailwindMarkup = [
|
|
7
|
-
"components/ItemCard.astro",
|
|
8
|
-
"layouts/Header.astro",
|
|
9
|
-
"../templates/default/src/pages/submit.astro",
|
|
10
|
-
].map((file) => readFileSync(resolve(import.meta.dirname, file), "utf8")).join("\n");
|
|
11
|
-
describe("Astro theme contract", () => {
|
|
12
|
-
it("keeps Starlight's light and dark foundation values", () => {
|
|
13
|
-
expect(astroTheme).toContain("--grove-background: oklch(100% 0 0)");
|
|
14
|
-
expect(astroTheme).toContain("--grove-foreground: var(--color-ink-950)");
|
|
15
|
-
expect(astroTheme).toContain("--grove-background: var(--color-ink-950)");
|
|
16
|
-
expect(astroTheme).toContain("--grove-foreground: var(--color-ink-50)");
|
|
17
|
-
expect(astroTheme).toContain("--color-ink-50: oklch(98.5% 0 0)");
|
|
18
|
-
expect(astroTheme).toContain("--color-ink-950: oklch(14.5% 0 0)");
|
|
19
|
-
});
|
|
20
|
-
it("exposes runtime semantic colors to Tailwind", () => {
|
|
21
|
-
for (const token of [
|
|
22
|
-
"background",
|
|
23
|
-
"foreground",
|
|
24
|
-
"primary",
|
|
25
|
-
"secondary",
|
|
26
|
-
"muted",
|
|
27
|
-
"accent",
|
|
28
|
-
"border",
|
|
29
|
-
"card",
|
|
30
|
-
"popover",
|
|
31
|
-
]) {
|
|
32
|
-
expect(astroTheme).toContain(`--color-${token}: var(--grove-${token})`);
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
it("uses the same system and mono font stacks as the docs", () => {
|
|
36
|
-
expect(astroTheme).toContain('--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,');
|
|
37
|
-
expect(astroTheme).toContain("--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,");
|
|
38
|
-
expect(astroTheme).toContain("font-family: var(--font-sans)");
|
|
39
|
-
});
|
|
40
|
-
it("does not duplicate package-owned foundation tokens in the scaffold", () => {
|
|
41
|
-
expect(scaffoldTheme).not.toContain("--color-ink-");
|
|
42
|
-
expect(scaffoldTheme).not.toContain("font-family:");
|
|
43
|
-
expect(scaffoldTheme).not.toContain("color-scheme:");
|
|
44
|
-
});
|
|
45
|
-
it("styles components in HTML with Starlight-aligned Tailwind utilities", () => {
|
|
46
|
-
expect(tailwindMarkup).toContain("rounded-[calc(var(--radius)+0.25rem)]");
|
|
47
|
-
expect(tailwindMarkup).toContain("rounded-full");
|
|
48
|
-
expect(tailwindMarkup).toContain("min-h-5");
|
|
49
|
-
expect(tailwindMarkup).toContain("h-8");
|
|
50
|
-
expect(tailwindMarkup).toContain("focus-visible:ring-3");
|
|
51
|
-
expect(astroTheme).not.toContain(".grove-card");
|
|
52
|
-
expect(astroTheme).not.toContain(".grove-btn");
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
//# sourceMappingURL=theme.test.js.map
|
package/dist/theme.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"theme.test.js","sourceRoot":"","sources":["../src/theme.test.ts"],"names":[],"mappings":"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"}
|
package/src/theme.test.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
-
|
|
5
|
-
const astroTheme = readFileSync(resolve(import.meta.dirname, "styles.css"), "utf8");
|
|
6
|
-
const scaffoldTheme = readFileSync(
|
|
7
|
-
resolve(import.meta.dirname, "../templates/default/src/styles/global.css"),
|
|
8
|
-
"utf8",
|
|
9
|
-
);
|
|
10
|
-
const tailwindMarkup = [
|
|
11
|
-
"components/ItemCard.astro",
|
|
12
|
-
"layouts/Header.astro",
|
|
13
|
-
"../templates/default/src/pages/submit.astro",
|
|
14
|
-
].map((file) => readFileSync(resolve(import.meta.dirname, file), "utf8")).join("\n");
|
|
15
|
-
|
|
16
|
-
describe("Astro theme contract", () => {
|
|
17
|
-
it("keeps Starlight's light and dark foundation values", () => {
|
|
18
|
-
expect(astroTheme).toContain("--grove-background: oklch(100% 0 0)");
|
|
19
|
-
expect(astroTheme).toContain("--grove-foreground: var(--color-ink-950)");
|
|
20
|
-
expect(astroTheme).toContain("--grove-background: var(--color-ink-950)");
|
|
21
|
-
expect(astroTheme).toContain("--grove-foreground: var(--color-ink-50)");
|
|
22
|
-
expect(astroTheme).toContain("--color-ink-50: oklch(98.5% 0 0)");
|
|
23
|
-
expect(astroTheme).toContain("--color-ink-950: oklch(14.5% 0 0)");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("exposes runtime semantic colors to Tailwind", () => {
|
|
27
|
-
for (const token of [
|
|
28
|
-
"background",
|
|
29
|
-
"foreground",
|
|
30
|
-
"primary",
|
|
31
|
-
"secondary",
|
|
32
|
-
"muted",
|
|
33
|
-
"accent",
|
|
34
|
-
"border",
|
|
35
|
-
"card",
|
|
36
|
-
"popover",
|
|
37
|
-
]) {
|
|
38
|
-
expect(astroTheme).toContain(`--color-${token}: var(--grove-${token})`);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("uses the same system and mono font stacks as the docs", () => {
|
|
43
|
-
expect(astroTheme).toContain(
|
|
44
|
-
'--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,',
|
|
45
|
-
);
|
|
46
|
-
expect(astroTheme).toContain(
|
|
47
|
-
"--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,",
|
|
48
|
-
);
|
|
49
|
-
expect(astroTheme).toContain("font-family: var(--font-sans)");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("does not duplicate package-owned foundation tokens in the scaffold", () => {
|
|
53
|
-
expect(scaffoldTheme).not.toContain("--color-ink-");
|
|
54
|
-
expect(scaffoldTheme).not.toContain("font-family:");
|
|
55
|
-
expect(scaffoldTheme).not.toContain("color-scheme:");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("styles components in HTML with Starlight-aligned Tailwind utilities", () => {
|
|
59
|
-
expect(tailwindMarkup).toContain("rounded-[calc(var(--radius)+0.25rem)]");
|
|
60
|
-
expect(tailwindMarkup).toContain("rounded-full");
|
|
61
|
-
expect(tailwindMarkup).toContain("min-h-5");
|
|
62
|
-
expect(tailwindMarkup).toContain("h-8");
|
|
63
|
-
expect(tailwindMarkup).toContain("focus-visible:ring-3");
|
|
64
|
-
expect(astroTheme).not.toContain(".grove-card");
|
|
65
|
-
expect(astroTheme).not.toContain(".grove-btn");
|
|
66
|
-
});
|
|
67
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Bug report
|
|
3
|
-
about: Something is broken on the site
|
|
4
|
-
title: "[bug] "
|
|
5
|
-
labels: ["bug"]
|
|
6
|
-
assignees: []
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## What happened
|
|
10
|
-
|
|
11
|
-
A clear, concise description of the bug.
|
|
12
|
-
|
|
13
|
-
## Steps to reproduce
|
|
14
|
-
|
|
15
|
-
1. Go to '…'
|
|
16
|
-
2. Click on '…'
|
|
17
|
-
3. Scroll down to '…'
|
|
18
|
-
4. See error
|
|
19
|
-
|
|
20
|
-
## What I expected
|
|
21
|
-
|
|
22
|
-
A clear, concise description of what you expected to happen.
|
|
23
|
-
|
|
24
|
-
## What I saw
|
|
25
|
-
|
|
26
|
-
Screenshots, error messages, console output — anything that helps.
|
|
27
|
-
|
|
28
|
-
## Environment
|
|
29
|
-
|
|
30
|
-
- Browser:
|
|
31
|
-
- OS:
|
|
32
|
-
- Page URL:
|
|
33
|
-
- Viewport size (rough):
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Feature request
|
|
3
|
-
about: Suggest an improvement to the site
|
|
4
|
-
title: "[feature] "
|
|
5
|
-
labels: ["enhancement"]
|
|
6
|
-
assignees: []
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## The problem
|
|
10
|
-
|
|
11
|
-
What's missing or awkward today?
|
|
12
|
-
|
|
13
|
-
## The proposed solution
|
|
14
|
-
|
|
15
|
-
What would you like to see? Mockups, links, references — anything
|
|
16
|
-
that helps us understand the shape of the change.
|
|
17
|
-
|
|
18
|
-
## Alternatives considered
|
|
19
|
-
|
|
20
|
-
Any other approaches you weighed, and why this one wins.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Submit a record
|
|
3
|
-
about: Suggest a new entry for the directory
|
|
4
|
-
title: "[submit] "
|
|
5
|
-
labels: ["submission"]
|
|
6
|
-
assignees: []
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
Thanks for taking the time to suggest a new entry! Please fill in the
|
|
10
|
-
template below — the more complete it is, the faster it lands.
|
|
11
|
-
|
|
12
|
-
**Important:** this issue tracker is for **suggestions**, not for the
|
|
13
|
-
actual data. Once approved, a maintainer will add the record to
|
|
14
|
-
`data/records/<slug>.yml` (or invite you to open a PR).
|
|
15
|
-
|
|
16
|
-
## 1. Basics
|
|
17
|
-
|
|
18
|
-
- **Name:**
|
|
19
|
-
- **One-line description:**
|
|
20
|
-
- **Category:** (e.g. Mobile, Web, DevOps, AI/ML)
|
|
21
|
-
- **Primary stack:** (e.g. Flutter, Vue, Go, Python)
|
|
22
|
-
- **Platforms:** (e.g. iOS, Android, Web, Linux, Self-host)
|
|
23
|
-
- **Project type:** (real-app, production, reference, library, tool,
|
|
24
|
-
demo, template, historical)
|
|
25
|
-
- **Difficulty:** (beginner, intermediate, advanced)
|
|
26
|
-
|
|
27
|
-
## 2. Links
|
|
28
|
-
|
|
29
|
-
- **Repository URL:** (the canonical `https://github.com/owner/repo`)
|
|
30
|
-
- **Website / homepage:**
|
|
31
|
-
- **Docs:**
|
|
32
|
-
|
|
33
|
-
## 3. Why it deserves a spot
|
|
34
|
-
|
|
35
|
-
A short paragraph — what makes it stand out, what it's good for, who
|
|
36
|
-
should look at it.
|
|
37
|
-
|
|
38
|
-
## 4. Caveats
|
|
39
|
-
|
|
40
|
-
Anything we should know about: license quirks, abandoned status,
|
|
41
|
-
paid-only features, etc.
|
|
42
|
-
|
|
43
|
-
## 5. Checkboxes
|
|
44
|
-
|
|
45
|
-
- [ ] The repo is **public** on GitHub / another public forge
|
|
46
|
-
- [ ] It's **open source** under an OSI-approved license
|
|
47
|
-
- [ ] It has been **maintained** in the last 12 months
|
|
48
|
-
- [ ] I am **not** the author (or I'm disclosing that I am)
|