astro-contributors 0.5.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/CHANGELOG.md +53 -0
- package/README.md +15 -0
- package/components/ContributorList.astro +15 -0
- package/components/FacePile.astro +87 -0
- package/index.ts +1 -0
- package/libs/getContributors.ts +105 -0
- package/libs/util-server.ts +45 -0
- package/package.json +36 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# astro-contributors
|
|
2
|
+
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#70](https://github.com/trueberryless-org/astro-contributors/pull/70) [`3788107`](https://github.com/trueberryless-org/astro-contributors/commit/37881073106047c17d1a17a87776244f322e7f40) Thanks [@trueberryless](https://github.com/trueberryless)! - Rename package from starlight-contributor-list to astro-contributors
|
|
8
|
+
|
|
9
|
+
## 0.4.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`0d1841f`](https://github.com/trueberryless-org/astro-contributors/commit/0d1841f3df224c12523384487a4f88fdc7950f45) Thanks [@trueberryless](https://github.com/trueberryless)! - Remove unnecessary dependencies, peer dependencies and dev dependencies
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- [#66](https://github.com/trueberryless-org/astro-contributors/pull/66) [`5014fc1`](https://github.com/trueberryless-org/astro-contributors/commit/5014fc1506028ebbc6f6ea129c7d0a9965cb90ee) Thanks [@trueberryless](https://github.com/trueberryless)! - Fine tune package metadata
|
|
18
|
+
|
|
19
|
+
## 0.3.2
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- [#60](https://github.com/trueberryless-org/astro-contributors/pull/60) [`ad2c493`](https://github.com/trueberryless-org/astro-contributors/commit/ad2c4933a3a049916d17d5fabba5f204fa8ac376) Thanks [@trueberryless](https://github.com/trueberryless)! - Fix keyboard focus accessibility issues.
|
|
24
|
+
|
|
25
|
+
## 0.3.1
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- [#51](https://github.com/trueberryless-org/astro-contributors/pull/51) [`57dc151`](https://github.com/trueberryless-org/astro-contributors/commit/57dc15123e9f0132b29ce9e47e0fb8dc91f2be49) Thanks [@trueberryless-org](https://github.com/apps/trueberryless-org)! - Setups trusted publishing using OpenID Connect (OIDC) authentication — no code changes.
|
|
30
|
+
|
|
31
|
+
## 0.3.0
|
|
32
|
+
|
|
33
|
+
### Minor Changes
|
|
34
|
+
|
|
35
|
+
- [#24](https://github.com/trueberryless-org/astro-contributors/pull/24) [`368e54d`](https://github.com/trueberryless-org/astro-contributors/commit/368e54d14c5277bbfce227f835715532bd5067c8) Thanks [@trueberryless](https://github.com/trueberryless)! - Add `ignore` parameter option
|
|
36
|
+
|
|
37
|
+
## 0.2.1
|
|
38
|
+
|
|
39
|
+
### Patch Changes
|
|
40
|
+
|
|
41
|
+
- [#19](https://github.com/trueberryless-org/astro-contributors/pull/19) [`4f439a3`](https://github.com/trueberryless-org/astro-contributors/commit/4f439a38bed4c3bbdde1891c65c37066fa033363) Thanks [@trueberryless](https://github.com/trueberryless)! - Update to Starlight 0.32
|
|
42
|
+
|
|
43
|
+
## 0.2.0
|
|
44
|
+
|
|
45
|
+
### Minor Changes
|
|
46
|
+
|
|
47
|
+
- [#9](https://github.com/trueberryless-org/astro-contributors/pull/9) [`128f46c`](https://github.com/trueberryless-org/astro-contributors/commit/128f46cca2b8c98abade03e5a3dee64970b367ae) Thanks [@trueberryless](https://github.com/trueberryless)! - Adds support for Astro v5, drops support for Astro v4.
|
|
48
|
+
|
|
49
|
+
⚠️ **BREAKING CHANGE:** The minimum supported version of Starlight is now `0.30.0`.
|
|
50
|
+
|
|
51
|
+
Please follow the [upgrade guide](https://github.com/withastro/starlight/releases/tag/%40astrojs/starlight%400.30.0) to update your project.
|
|
52
|
+
|
|
53
|
+
Note that the [`legacy.collections` flag](https://docs.astro.build/en/reference/legacy-flags/#collections) is not supported by this plugin and you should update your collections to use Astro's new Content Layer API.
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# `astro-contributors`
|
|
2
|
+
|
|
3
|
+
Display a list of all contributors to your project
|
|
4
|
+
|
|
5
|
+
## Documentation
|
|
6
|
+
|
|
7
|
+
Want to get started immediately?
|
|
8
|
+
|
|
9
|
+
Check out the `astro-contributors` getting started guide.
|
|
10
|
+
|
|
11
|
+
## License
|
|
12
|
+
|
|
13
|
+
Licensed under the MIT license, Copyright © trueberryless.
|
|
14
|
+
|
|
15
|
+
See [LICENSE](/LICENSE) for more information.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getAllContributors } from "../libs/getContributors";
|
|
3
|
+
import FacePile from "./FacePile.astro";
|
|
4
|
+
|
|
5
|
+
export interface Props {
|
|
6
|
+
githubRepo?: `${string}/${string}`;
|
|
7
|
+
ignore?: string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { githubRepo = "withastro/docs", ignore } = Astro.props as Props;
|
|
11
|
+
|
|
12
|
+
const contributors = await getAllContributors(githubRepo, ignore);
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<FacePile {contributors} />
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Contributor } from "../libs/getContributors";
|
|
3
|
+
|
|
4
|
+
export interface Props {
|
|
5
|
+
contributors: Contributor[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { contributors } = Astro.props as Props;
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<!-- Thanks to @5t3ph for https://smolcss.dev/#smol-avatar-list! -->
|
|
12
|
+
<div>
|
|
13
|
+
<ul class="avatar-list">
|
|
14
|
+
{
|
|
15
|
+
contributors.map((item) => (
|
|
16
|
+
<li>
|
|
17
|
+
<a href={`https://github.com/${item.login}`}>
|
|
18
|
+
<img
|
|
19
|
+
alt={item.login}
|
|
20
|
+
title={item.login}
|
|
21
|
+
width="3rem"
|
|
22
|
+
height="3rem"
|
|
23
|
+
src={`https://avatars.githubusercontent.com/u/${item.id}?s=64`}
|
|
24
|
+
loading="lazy"
|
|
25
|
+
/>
|
|
26
|
+
</a>
|
|
27
|
+
</li>
|
|
28
|
+
))
|
|
29
|
+
}
|
|
30
|
+
</ul>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<style>
|
|
34
|
+
.avatar-list {
|
|
35
|
+
--avatar-size: 3rem;
|
|
36
|
+
--avatar-overlap: -0.125em;
|
|
37
|
+
--avatar-row-spacing: 0.125em;
|
|
38
|
+
--avatar-outline-width: 1px;
|
|
39
|
+
--avatar-outline-offset: 0.08em;
|
|
40
|
+
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-wrap: wrap;
|
|
43
|
+
list-style: none;
|
|
44
|
+
padding: 0rem;
|
|
45
|
+
font-size: var(--avatar-size);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.avatar-list li {
|
|
49
|
+
position: relative;
|
|
50
|
+
--avatar-row-margin: calc(
|
|
51
|
+
var(--avatar-outline-offset) + var(--avatar-outline-width) +
|
|
52
|
+
var(--avatar-row-spacing) / 2
|
|
53
|
+
);
|
|
54
|
+
margin: var(--avatar-row-margin) var(--avatar-overlap)
|
|
55
|
+
var(--avatar-row-margin) 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.avatar-list li:has(:focus-visible) {
|
|
59
|
+
z-index: 10;
|
|
60
|
+
transform: scale(1.1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.avatar-list img,
|
|
64
|
+
.avatar-list a {
|
|
65
|
+
display: block;
|
|
66
|
+
border-radius: 50%;
|
|
67
|
+
width: var(--avatar-size);
|
|
68
|
+
height: var(--avatar-size);
|
|
69
|
+
/* Hide alt/title if Avatar image fails to load. */
|
|
70
|
+
text-decoration: none;
|
|
71
|
+
color: transparent;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.avatar-list img {
|
|
75
|
+
width: 100%;
|
|
76
|
+
height: 100%;
|
|
77
|
+
object-fit: cover;
|
|
78
|
+
background-color: var(--sl-color-accent);
|
|
79
|
+
/* Indicates the contributor image boundaries for forced colors users, transparent is changed to a solid color */
|
|
80
|
+
outline: 1px solid transparent;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.avatar-list a:focus-visible {
|
|
84
|
+
outline: 2px solid var(--sl-color-accent);
|
|
85
|
+
outline-offset: var(--avatar-outline-offset);
|
|
86
|
+
}
|
|
87
|
+
</style>
|
package/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ContributorList } from "./components/ContributorList.astro";
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { cachedFetch } from "./util-server";
|
|
2
|
+
|
|
3
|
+
export interface Contributor {
|
|
4
|
+
login: string;
|
|
5
|
+
id: number;
|
|
6
|
+
contributions: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Commit {
|
|
10
|
+
author: {
|
|
11
|
+
login: string;
|
|
12
|
+
id: number;
|
|
13
|
+
};
|
|
14
|
+
commit: {
|
|
15
|
+
message: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const printError = (e: Error) =>
|
|
20
|
+
console.warn(
|
|
21
|
+
`[error] /src/util/getContributors.ts\n ${e?.message ?? e}`
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
async function recursiveFetch(endpoint: string, page = 1) {
|
|
25
|
+
try {
|
|
26
|
+
const queryParam = endpoint.includes("?") ? "&" : "?";
|
|
27
|
+
const pageSize = 100;
|
|
28
|
+
const url = `https://api.github.com/${endpoint}${queryParam}per_page=${pageSize}&page=${page}`;
|
|
29
|
+
|
|
30
|
+
const token = import.meta.env.PUBLIC_GITHUB_TOKEN;
|
|
31
|
+
|
|
32
|
+
const res = await cachedFetch(
|
|
33
|
+
url,
|
|
34
|
+
{
|
|
35
|
+
method: "GET",
|
|
36
|
+
headers: {
|
|
37
|
+
Authorization:
|
|
38
|
+
token && `Basic ${Buffer.from(token, "binary").toString("base64")}`,
|
|
39
|
+
"User-Agent": "astro-docs/1.0",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{ duration: "15m" }
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const data = await res.json();
|
|
46
|
+
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Request to fetch endpoint failed. Reason: ${res.statusText}
|
|
50
|
+
Message: ${data?.message}`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Fetch more data recursively if there are more than GitHub’s per-page response limit.
|
|
55
|
+
if (data.length === pageSize) {
|
|
56
|
+
const rest = await recursiveFetch(endpoint, page + 1);
|
|
57
|
+
data.push(...rest);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return data;
|
|
61
|
+
} catch (e) {
|
|
62
|
+
printError(e as Error);
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function getAllContributors(repo: string, ignore: string[] = []) {
|
|
68
|
+
const endpoint = `repos/${repo}/contributors`;
|
|
69
|
+
const contributors: Contributor[] = await recursiveFetch(endpoint);
|
|
70
|
+
|
|
71
|
+
return contributors.filter(({ login }) => !ignore.includes(login));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function getTranslationContributors(
|
|
75
|
+
paths: string[],
|
|
76
|
+
repo: string
|
|
77
|
+
) {
|
|
78
|
+
const ignoredCommitKeywords =
|
|
79
|
+
/(en-only|typo|broken link|i18nReady|i18nIgnore)/i;
|
|
80
|
+
const contributors: Contributor[] = [];
|
|
81
|
+
|
|
82
|
+
for (const path of paths) {
|
|
83
|
+
const endpoint = `repos/${repo}/commits?path=${path}`;
|
|
84
|
+
const commits: Commit[] = await recursiveFetch(endpoint);
|
|
85
|
+
|
|
86
|
+
for (const { author, commit } of commits) {
|
|
87
|
+
if (ignoredCommitKeywords.test(commit.message) || !author) continue;
|
|
88
|
+
const contributor = contributors.find(
|
|
89
|
+
(contributor) => contributor.id === author.id
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (!contributor) {
|
|
93
|
+
contributors.push({
|
|
94
|
+
id: author.id,
|
|
95
|
+
login: author.login,
|
|
96
|
+
contributions: 1,
|
|
97
|
+
});
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
contributor.contributions += 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return contributors.sort((a, b) => b.contributions - a.contributions);
|
|
105
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This file contains server-side utilities using features that don't work in a browser.
|
|
3
|
+
Do not import this file from a hydrated client-side component.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type CachedFetchOptions = {
|
|
7
|
+
duration?: string;
|
|
8
|
+
verbose?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export async function cachedFetch(
|
|
12
|
+
url: string,
|
|
13
|
+
fetchOptions = {},
|
|
14
|
+
{ duration = "5m", verbose = false }: CachedFetchOptions = {}
|
|
15
|
+
) {
|
|
16
|
+
let status = 200;
|
|
17
|
+
let statusText = "OK";
|
|
18
|
+
let buffer: Buffer | undefined;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const response = await fetch(url, fetchOptions);
|
|
22
|
+
status = response.status;
|
|
23
|
+
statusText = response.statusText;
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
throw new Error(`Bad response for ${url} (${status}): ${statusText}`);
|
|
26
|
+
}
|
|
27
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
28
|
+
buffer = Buffer.from(arrayBuffer);
|
|
29
|
+
} catch (e: unknown) {
|
|
30
|
+
const error = e as Error;
|
|
31
|
+
const msg: string = error?.message || error.toString();
|
|
32
|
+
const matches = msg.match(/^Bad response for (.*) \(.*?\): (.*)$/);
|
|
33
|
+
status = parseInt(matches?.[2] || "") || 404;
|
|
34
|
+
statusText = matches?.[3] || msg;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
ok: status >= 200 && status <= 299,
|
|
39
|
+
status,
|
|
40
|
+
statusText,
|
|
41
|
+
body: buffer,
|
|
42
|
+
json: () => buffer && JSON.parse(buffer.toString()),
|
|
43
|
+
text: () => buffer?.toString(),
|
|
44
|
+
};
|
|
45
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "astro-contributors",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Display a list of all contributors to your project",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"starlight",
|
|
7
|
+
"docs",
|
|
8
|
+
"contributors",
|
|
9
|
+
"component"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://astro-contributors.trueberryless.org/",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/trueberryless-org/astro-contributors/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/trueberryless-org/astro-contributors.git",
|
|
18
|
+
"directory": "packages/astro-contributors"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "trueberryless <trueberryless@gmail.com> (https://trueberryless.org)",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": "./index.ts",
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"astro": "^5.0.0"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=22.12.0"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
}
|
|
36
|
+
}
|