Skip to content

Commit

Permalink
chore: release [email protected]
Browse files Browse the repository at this point in the history
  • Loading branch information
lin-stephanie committed Dec 13, 2024
1 parent 38cf8fc commit 25f92a8
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 71 deletions.
8 changes: 8 additions & 0 deletions .changeset/dirty-goats-prove.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"astro-loader-github-prs": minor
---

Add `monthsBack` option to specify the recent months for loading pull requests
Support returning the `<Content />` component via `render(entry)` to render the PR content
Errors no longer force the entire Astro project to terminate
No longer calls `store.clear()` internally
72 changes: 42 additions & 30 deletions packages/astro-loader-github-prs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![jsDocs.io][jsdocs-src]][jsdocs-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]

This package provides a GitHub PRs loader for Astro, fetching pull requests via a GitHub search query for use in Astro projects.
This package provides a GitHub PRs loader for Astro, fetching pull requests with a search query for use in Astro projects.

## Installation

Expand All @@ -21,55 +21,62 @@ export default defineConfig({
experimental: {
contentLayer: true,
},
});
})
```

In `src/content/config.ts`, import and configure the GitHub PRs loader to define a new content collection:
In `src/content/config.ts` (for `^4.14.0`) or `src/content.config.ts` (for `^5.0.0`), import and configure the GitHub PRs loader to define a new content collection:

```ts
import { defineCollection } from "astro:content";
import { githubPrsLoader } from "astro-loader-github-prs";
import { defineCollection } from "astro:content"
import { githubPrsLoader } from "astro-loader-github-prs"

const githubPrs = defineCollection({
loader: githubPrsLoader({
search: 'author:username created:>=2024-10-01',
}),
});
})

export const collections = { githubPrs };
export const collections = { githubPrs }
```

[Query the content collection](https://docs.astro.build/en/guides/content-collections/#querying-collections) like any other Astro content collection to render the loaded GitHub PRs:

```astro
---
import { getCollection } from "astro:content";
import { getCollection } from "astro:content"
const prs = await getCollection("githubPrs");
const prs = await getCollection("githubPrs")
---
<ul>
{
prs.map((pr) => (
<li>
<a href={pr.data.url}>{pr.data.title} - {pr.data.repository.nameWithOwner}</a>
</li>
))
}
</ul>
{
prs.map(async (pr) => {
const { Content } = await render(pr)
return (
<div>
<a href={pr.data.url}>
{pr.data.repository.nameWithOwner} - #{pr.data.number}
</a>
<p set:html={pr.data.titleHTML} />
<Content />
{/* <div set:html={pr.data.bodyHTML}></div> */}
</div>
)
})
}
```

To update the data, trigger a site rebuild, as [the loader fetches data only at build time](https://docs.astro.build/en/reference/content-loader-reference/#object-loaders).
To update the data, trigger a site rebuild (e.g., using a third-party cron job service), as [the loader fetches data only at build time](https://docs.astro.build/en/reference/content-loader-reference/#object-loaders).

## Configuration

The loader fetches PRs via the GitHub GraphQL API with a search string, requiring a `repo`-scoped PAT, and returns up to 1,000 results. Options include:
The loader fetches PRs via the GitHub GraphQL API with a search string, requiring a repo-scoped PAT, and [returns up to 1,000 results](https://docs.github.com/en/graphql/reference/objects#searchresultitemconnection). Options include:

| Option (* required) | Type (defaults) | Description |
| ------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `search`* | `string` | The search string for querying pull requests on GitHub. This string will be concatenated with `type:pr` to form the complete search query. See [how to search pull requests](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests). For examples:<br>`'author:xxx created:>=2024-01-01'`: matches prs written by xxx that were created after 2024.<br>`'author:xxx -user:xxx'`: matches prs written by xxx, but not to their own repositories. |
| `githubToken` | `string` (defaults: `'import.meta.env.GITHUB_TOKEN'`) | A GitHub PAT with at least `repo` scope permissions. Defaults to the `GITHUB_TOKEN` environment variable. **If configured here, keep confidential and avoid public exposure.** See [how to create one](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) and [configure env vars in an Astro project](https://docs.astro.build/en/guides/environment-variables/#setting-environment-variables). |

| Option (* required) | Type (default) | Description |
| ------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `search`* | `string` | The search string for querying pull requests on GitHub. This string will be concatenated with `type:pr` to form the complete search query. See [how to search pull requests](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests). For examples:<br>`'author:xxx created:>=2024-01-01'`: matches prs written by xxx that were created after 2024.<br>`'author:xxx -user:xxx'`: matches prs written by xxx, but not to their own repositories. |
| `monthsBack` | `number` | The number of recent months to load pull requests, including the current month. The loader automatically converts this to a date for the 'created' qualifier in the search query. If the `'created'` qualifier is defined in search option, it will override this value. |
| `githubToken` | `string` (Defaults to the `GITHUB_TOKEN` environment variable) | A GitHub PAT with at least `repo` scope permissions. Defaults to the `GITHUB_TOKEN` environment variable. **If configured here, keep confidential and avoid public exposure.** See [how to create one](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) and [configure env vars in an Astro project](https://docs.astro.build/en/guides/environment-variables/#setting-environment-variables). |

## Schema

Expand Down Expand Up @@ -109,15 +116,20 @@ const GithubPrSchema = z.object({
})
```

Astro automatically applies this schema to generate TypeScript interfaces, providing full support for autocompletion and type-checking when querying the collection.
Astro automatically applies these schemas to generate TypeScript interfaces, enabling autocompletion and type-checking for collection queries. If you [customize the collection schema](https://docs.astro.build/en/guides/content-collections/#defining-the-collection-schema), ensure compatibility with the loader's built-in Zod schema to prevent errors. For additional fields, consider opening an issue.

## Changelog

If you need to [customize the collection schema](https://docs.astro.build/en/guides/content-collections/#defining-the-collection-schema), ensure it remains compatible with the built-in Zod schema of the loader to avoid errors. For additional fields you'd like to fetch, feel free to [open an issue](https://github.com/lin-stephanie/astro-loaders/issues).
See [CHANGELOG.md](CHANGELOG.md) for the change history of this loader.

## Contribution

[version-badge]: https://img.shields.io/npm/v/astro-loader-github-prs?label=release&style=flat&colorA=080f12&colorB=ef7575
If you see any errors or room for improvement, feel free to open an [issues](https://github.com/lin-stephanie/astro-loaders/issues) or [pull request](https://github.com/lin-stephanie/astro-loaders/pulls) . Thank you in advance for contributing! ❤️


[version-badge]: https://img.shields.io/npm/v/astro-loader-github-prs?label=release&style=flat&colorA=080f12&colorB=f87171
[version-link]: https://www.npmjs.com/package/astro-loader-github-prs
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=ef7575
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=f87171
[jsdocs-href]: https://www.jsdocs.io/package/astro-loader-github-prs
[npm-downloads-src]: https://img.shields.io/npm/dm/astro-loader-github-prs?style=flat&colorA=080f12&colorB=ef7575
[npm-downloads-src]: https://img.shields.io/npm/dm/astro-loader-github-prs?style=flat&colorA=080f12&colorB=f87171
[npm-downloads-href]: https://npmjs.com/package/astro-loader-github-prs

2 changes: 1 addition & 1 deletion packages/astro-loader-github-prs/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "astro-loader-github-prs",
"version": "1.0.2",
"description": "Aatro loader for loading GitHub pull reuqests from a given search string.",
"description": "Astro loader for loading GitHub pull requests with a search query.",
"author": "Stephanie Lin <[email protected]>",
"license": "MIT",
"keywords": [
Expand Down
17 changes: 16 additions & 1 deletion packages/astro-loader-github-prs/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@ export const GithubPrsLoaderConfigSchema = z.object({
*/
search: z.string(),

/**
* The number of recent months to load pull requests, including the current month.
* The loader automatically converts this to a date for the 'created' qualifier in the search query.
*
* If the {@link https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests#search-by-when-an-issue-or-pull-request-was-created-or-last-updated 'created'}
* qualifier is defined in `search` option, it will override this value.
*
* For example, setting to `3` on December 4, 2024, would yield: 'type:pr created:>=2024-10-01 ...'.
*/
monthsBack: z
.number()
.int({ message: '`monthsBack` must be an integer' })
.positive({ message: '`monthsBack` must be a positive integer' })
.optional(),

/**
* You need to {@link https://github.com/settings/tokens create a GitHub PAT}
* with at least `repo` scope permissions to authenticate requests to the GraphQL API.
Expand All @@ -27,7 +42,7 @@ export const GithubPrsLoaderConfigSchema = z.object({
* - {@link https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic How to create a GitHub PAT (classic)}
* - {@link https://docs.astro.build/en/guides/environment-variables/#setting-environment-variables How to store GitHub PAT in Astro project environment variables}
*/
githubToken: z.string().default(import.meta.env.GITHUB_TOKEN),
githubToken: z.string().optional(),
})

export type GithubPrsLoaderUserConfig = z.input<
Expand Down
86 changes: 47 additions & 39 deletions packages/astro-loader-github-prs/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,72 @@
import { AstroError } from 'astro/errors'
import { Octokit } from 'octokit'

import { readFileSync } from 'node:fs'

import { Octokit } from 'octokit'

import pkg from '../package.json' with { type: 'json' }
import { GithubPrsLoaderConfigSchema } from './config.js'
import { GithubPrSchema } from './schema.js'
import { getQueryWithMonthsBack } from './utils.js'

import type { Loader } from 'astro/loaders'
import type { GithubPrsLoaderUserConfig } from './config.js'
import type { GetPrsQuery, GetPrsQueryVariables } from './graphql/types.js'
import type { GithubPr } from './schema.js'

/**
* Aatro loader for loading GitHub pull reuqests from a given search string.
* Astro loader for loading GitHub pull requests with a search query.
*
* @see https://github.com/lin-stephanie/astro-loaders/tree/main/packages/astro-loader-github-prs
*/
function githubPrsLoader(userConfig: GithubPrsLoaderUserConfig): Loader {
const parsedConfig = GithubPrsLoaderConfigSchema.safeParse(userConfig)
if (!parsedConfig.success) {
throw new AstroError(
`The configuration provided in '${pkg.name}' is invalid. Refer to the following error report or access configuration details for this loader here: ${pkg.homepage}#configuration.`,
`${parsedConfig.error.issues.map((issue) => issue.message).join('\n')}`
)
}
const parsedUserConfig = parsedConfig.data

return {
name: pkg.name,
schema: GithubPrSchema,
async load({ logger, store, parseData }) {
const { search, githubToken } = parsedUserConfig
const prs: GithubPr[] = []
const query = `type:pr ${search}`
logger.info(
`Loading GitHub pull reuqests based on the search string: '${query}'`
)
async load({ logger, store, parseData, generateDigest }) {
const parsedConfig = GithubPrsLoaderConfigSchema.safeParse(userConfig)
if (!parsedConfig.success) {
logger.error(
`The configuration provided is invalid. ${parsedConfig.error.issues.map((issue) => issue.message).join('\n')}.
Check out the configuration: ${pkg.homepage}README.md#configuration.`
)
return
}

const { search, monthsBack, githubToken } = parsedConfig.data
const token = githubToken || import.meta.env.GITHUB_TOKEN

if (search.length === 0) {
logger.warn(
'Search string is empty and no pull requests will be loaded'
)
return
}

if (!token) {
throw new AstroError(
'No GitHub token provided. Please provide a `githubToken` or set the `GITHUB_TOKEN` environment variable.',
`How to create a GitHub PAT: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic.
How to store GitHub PAT in Astro project environment variables: https://docs.astro.build/en/guides/environment-variables/#setting-environment-variables`
logger.error(
'No GitHub token provided. Please provide a `githubToken` or set the `GITHUB_TOKEN` environment variable.\nHow to create a GitHub PAT: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic.\nHow to store token in Astro project environment variables: https://docs.astro.build/en/guides/environment-variables/#setting-environment-variables.'
)
return
}
const octokit = new Octokit({
auth: githubToken || import.meta.env.GITHUB_TOKEN,
})

const prs: GithubPr[] = []
const parsedSearch = getQueryWithMonthsBack(search, monthsBack)
const getPrsQuery = readFileSync(
new URL('./graphql/query.graphql', import.meta.url),
'utf8'
)
const octokit = new Octokit({ auth: token })

logger.info(
`Loading GitHub pull requests with a search query: '${parsedSearch}'`
)

try {
let hasNextPage = true
let cursor: string | null = null

while (hasNextPage) {
const variables: GetPrsQueryVariables = {
search: query,
search: parsedSearch,
first: 100,
cursor,
}
Expand Down Expand Up @@ -115,20 +120,23 @@ How to store GitHub PAT in Astro project environment variables: https://docs.ast
hasNextPage = res.search.pageInfo.hasNextPage || false
cursor = res.search.pageInfo.endCursor || null
}
} catch (error) {
throw new AstroError(
`Failed to load GitHub pull requests: ${(error as Error).message}`
)
}

store.clear()
for (const item of prs) {
const parsedItem = await parseData({ id: item.id, data: item })
store.set({
id: item.id,
data: parsedItem,
rendered: { html: item.bodyHTML },
digest: generateDigest(parsedItem),
})
}

for (const item of prs) {
const parsedItem = await parseData({ id: item.id, data: item })
store.set({ id: item.id, data: parsedItem })
logger.info('Successfully loaded GitHub pull requests')
} catch (error) {
logger.error(
`Failed to load GitHub pull requests. ${(error as Error).message}`
)
}

logger.info('Successfully loaded GitHub pull requests')
},
}
}
Expand Down
16 changes: 16 additions & 0 deletions packages/astro-loader-github-prs/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Get the search query with `monthsBack`.
*/
export function getQueryWithMonthsBack(
search: string,
monthsBack: number | undefined
) {
if (!search.includes('created') && monthsBack) {
const startDate = new Date()
startDate.setMonth(startDate.getMonth() - monthsBack + 1)
startDate.setDate(1)

return `${search} created:>=${startDate.toISOString().split('T')[0]}`
}
return search
}

0 comments on commit 25f92a8

Please sign in to comment.