Skip to content

Commit 507be69

Browse files
authored
feat(github-commits): add component, playground page (#40)
* feat(github-commits): add component, playground page * fix: vue/no-watch-after-await
1 parent c15dfef commit 507be69

File tree

10 files changed

+204
-6
lines changed

10 files changed

+204
-6
lines changed

playground/components/Navigation.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ const nav = [
3333
{
3434
name: 'Github Link',
3535
path: '/github-link'
36+
},
37+
{
38+
name: 'Commits',
39+
path: '/commits'
3640
}
3741
]
3842
</script>

playground/pages/commits.vue

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<script setup lang="ts">
2+
import { ref } from 'vue'
3+
4+
const qOwner = ref('nuxt-community')
5+
const qRepo = ref('supabase-module')
6+
const file = ref('package.json')
7+
const date = ref('2022-06-30')
8+
</script>
9+
10+
<template>
11+
<div>
12+
<GithubCommits v-slot="{ commits, refresh }" :query="{ owner: qOwner, repo: qRepo, source: file, date }">
13+
<div style="margin-bottom: .5rem;">
14+
Repo: <input v-model="qOwner" type="text"> / <input v-model="qRepo" type="text">
15+
</div>
16+
<div style="margin-bottom: .5rem;">
17+
File: <input v-model="file" type="text">
18+
</div>
19+
<div style="margin-bottom: .5rem;">
20+
From date: <input v-model="date" type="text">
21+
</div>
22+
<button @click="refresh">
23+
Search
24+
</button>
25+
26+
<h3>Commit List</h3>
27+
<em v-if="!commits.length">No commit found</em>
28+
<div v-for="commit in commits" :key="commit.hash" style="display: flex; margin: 1rem 0">
29+
<a :href="`https://github.com/${qOwner}/${qRepo}/commit/${commit.hash}`" target="_blank">
30+
<code>{{ commit.hash.slice(0, 7) }}</code>
31+
</a>
32+
<span style="margin: 0 .5rem;">-</span>
33+
<span>
34+
<span v-html="commit.message" />
35+
36+
<div v-for="{ login, name } in commit.authors" :key="login">
37+
committed by <a :href="`https://github.com/${login}`" target="_blank">{{ name }}</a>
38+
</div>
39+
</span>
40+
</div>
41+
</GithubCommits>
42+
</div>
43+
</template>

src/module.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,22 @@ export default defineNuxtModule<ModuleOptions>({
200200
})
201201
}
202202

203+
// Setup commits API
204+
nitroConfig.handlers.push({
205+
route: '/api/_github/commits',
206+
handler: resolveModule('./server/api/commits', { paths: runtimeDir })
207+
}, {
208+
route: '/api/_github/commits/:query',
209+
handler: resolveModule('./server/api/commits', { paths: runtimeDir })
210+
})
211+
212+
// GithubCommits component
213+
addComponent({
214+
name: 'GithubCommits',
215+
filePath: resolveModule('./components/GithubCommits', { paths: runtimeDir }),
216+
global: true
217+
})
218+
203219
addImports({
204220
name: 'useGithub',
205221
from: resolveModule('./composables/useGithub', { paths: runtimeDir })
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { defineComponent, useSlots } from 'vue'
2+
import type { PropType } from 'vue'
3+
import { hash } from 'ohash'
4+
import { useGithub } from '../composables/useGithub'
5+
import { GithubCommitsQuery } from '../types'
6+
// @ts-ignore
7+
import { useAsyncData } from '#imports'
8+
9+
export default defineComponent({
10+
props: {
11+
query: {
12+
type: Object as PropType<GithubCommitsQuery>,
13+
required: false,
14+
default: () => ({})
15+
}
16+
},
17+
async setup (props) {
18+
const { fetchCommits } = useGithub()
19+
20+
const { data: commits, pending, refresh } = await useAsyncData(`github-commits-${hash(props.query)}`, () => fetchCommits(props.query))
21+
return {
22+
commits,
23+
pending,
24+
refresh
25+
}
26+
},
27+
render ({ commits, pending, refresh }) {
28+
const slots = useSlots()
29+
return slots?.default?.({ commits, pending, refresh })
30+
}
31+
})

src/runtime/components/GithubFileContributors.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ export default defineComponent({
1818
const source = toRef(props.query, 'source')
1919

2020
const { fetchFileContributors } = useGithub()
21+
watch(source, () => {
22+
if (refresh) { refresh() }
23+
})
2124

2225
const { data: contributors, refresh, pending } = await useAsyncData(`github-file-contributors-${hash(props.query)}`, () => fetchFileContributors(props.query))
2326

24-
watch(source, () => refresh())
25-
2627
return {
2728
contributors,
2829
refresh,

src/runtime/composables/useGithub.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
2-
import type { GithubRepositoryOptions, GithubContributorsQuery, GithubReleasesQuery, GithubRepository, GithubRawRelease, GithubRawContributor, GithubReleaseQuery } from '../types'
2+
import type { GithubRepositoryOptions, GithubContributorsQuery, GithubReleasesQuery, GithubRepository, GithubRawRelease, GithubRawContributor, GithubReleaseQuery, GithubCommitsQuery } from '../types'
33
import { useRequestEvent } from '#imports'
44

55
export const useGithub = () => {
@@ -45,14 +45,21 @@ export const useGithub = () => {
4545
return $fetch(url, { responseType: 'json' })
4646
}
4747

48+
const fetchCommits = (query: GithubCommitsQuery): Promise<any> => {
49+
const url = `/api/_github/commits/${encodeParams(query)}.json`
50+
process.server && addPrerenderPath(url)
51+
return $fetch(url, { responseType: 'json' })
52+
}
53+
4854
return {
4955
fetchRepository,
5056
fetchReleases,
5157
fetchRelease,
5258
fetchLastRelease,
5359
fetchContributors,
5460
fetchFileContributors,
55-
fetchReadme
61+
fetchReadme,
62+
fetchCommits
5663
}
5764
}
5865

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type { ModuleOptions } from '../../../../module'
2+
import { decodeParams, fetchCommits, overrideConfig } from '../../utils/queries'
3+
import { GithubCommitsQuery } from '../../../types'
4+
// @ts-ignore
5+
import * as imports from '#imports'
6+
7+
const moduleConfig: ModuleOptions = imports.useRuntimeConfig().github ?? {}
8+
9+
let handler
10+
if (process.env.NODE_ENV === 'development' || moduleConfig.disableCache) {
11+
// @ts-ignore
12+
// eslint-disable-next-line import/namespace
13+
handler = imports.defineEventHandler
14+
} else {
15+
// @ts-ignore
16+
// eslint-disable-next-line import/namespace
17+
handler = imports.defineCachedEventHandler
18+
}
19+
20+
export default handler(
21+
async (event: any) => {
22+
// Get query
23+
const query = decodeParams(event.context.params.query) as GithubCommitsQuery
24+
const normalizedQuery = {
25+
...query,
26+
date: query.date ? new Date(query.date) : undefined
27+
}
28+
29+
// Merge query in module config
30+
const githubConfig = overrideConfig(moduleConfig, query)
31+
32+
// Fetch contributors from GitHub
33+
return await fetchCommits(normalizedQuery, githubConfig)
34+
},
35+
{
36+
maxAge: 60 // cache for one minute
37+
}
38+
)

src/runtime/server/utils/queries.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { defu } from 'defu'
55
import type {
66
ModuleOptions
77
} from '../../../module'
8-
import { GithubRawRelease, GithubRepositoryOptions, GithubRawContributor, GithubContributorsQuery, GithubReleasesQuery, GithubRepositoryReadme, GithubRepository } from '../../types'
8+
import { GithubRawRelease, GithubRepositoryOptions, GithubRawContributor, GithubContributorsQuery, GithubReleasesQuery, GithubRepositoryReadme, GithubRepository, GithubCommitsQuery } from '../../types'
99
// @ts-ignore
1010
import { parseContent } from '#content/server'
1111

12-
export function decodeParams (params: string = '') {
12+
export function decodeParams (params = '') {
1313
const result = {}
1414
params = params.replace(/\.json$/, '')
1515
for (const param of params.split(':')) {
@@ -151,6 +151,57 @@ export async function fetchRepositoryContributors ({ max }: Partial<GithubContri
151151
return contributors.map(({ avatar_url, login }) => ({ avatar_url, login }))
152152
}
153153

154+
export async function fetchCommits ({ date, source }: Partial<Omit<GithubCommitsQuery, 'date'> & { date: Date }>, { owner, repo, branch, token }: GithubRepositoryOptions) {
155+
const daysAgo = () => {
156+
if (date) { return date.toISOString() }
157+
158+
const now = new Date()
159+
now.setDate(now.getDate() - 30) // get from 30 days ago
160+
return now.toISOString()
161+
}
162+
163+
const path = source ? `path: "${source}",` : ''
164+
const data = await githubGraphqlQuery(
165+
`
166+
query {
167+
repository(owner: "${owner}", name: "${repo}") {
168+
object(expression: "${branch}") {
169+
... on Commit {
170+
history(since: "${daysAgo()}", ${path}) {
171+
nodes {
172+
oid
173+
messageHeadlineHTML
174+
authors(first: ${5}) {
175+
nodes {
176+
user {
177+
name
178+
avatarUrl
179+
login
180+
}
181+
}
182+
}
183+
}
184+
}
185+
}
186+
}
187+
}
188+
}
189+
`, { token }
190+
)
191+
192+
if (!data?.repository?.object?.history?.nodes) { return [] }
193+
194+
const commits = data.repository.object.history.nodes.map(node => ({
195+
hash: node.oid,
196+
message: node.messageHeadlineHTML,
197+
authors: node.authors.nodes
198+
.map(author => author.user)
199+
.filter(user => user?.name && !isBot(user))
200+
}))
201+
202+
return commits
203+
}
204+
154205
export async function fetchFileContributors ({ source, max }: Partial<GithubContributorsQuery>, { owner, repo, branch, token }: GithubRepositoryOptions & { maxContributors?: number }) {
155206
const data = await githubGraphqlQuery(
156207
`

src/runtime/types/commits.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { GithubRepositoryOptions } from '.'
2+
3+
export interface GithubCommitsQuery extends GithubRepositoryOptions {
4+
date?: string
5+
source?: string
6+
}

src/runtime/types/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './repository'
22
export * from './releases'
33
export * from './contributors'
4+
export * from './commits'

0 commit comments

Comments
 (0)