diff --git a/README.md b/README.md index 73d785c..6745862 100644 --- a/README.md +++ b/README.md @@ -69,23 +69,10 @@ const series = defineCollection({ { description: ['html', 'markdown'], }, - { - operation: 'posts', - variables: { - first: { - name: 'first', - required: true, - value: 20, - type: 'Int', - }, - }, - fields: [{ - edges: [{ - node: ['slug', 'title', 'publishedAt', { coverImage: ['url'] }], - }], - }], - }, ], + withPosts: { + fields: ['slug', 'title', 'publishedAt', { coverImage: ['url'] }], + }, }), schema: z.object({ slug: z.string(), diff --git a/examples/hashnode/src/content/config.ts b/examples/hashnode/src/content/config.ts index 2817c22..0847b89 100644 --- a/examples/hashnode/src/content/config.ts +++ b/examples/hashnode/src/content/config.ts @@ -35,23 +35,10 @@ const series = defineCollection({ { description: ['html', 'markdown'], }, - { - operation: 'posts', - variables: { - first: { - name: 'first', - required: true, - value: 20, - type: 'Int', - }, - }, - fields: [{ - edges: [{ - node: ['slug', 'title', 'publishedAt', { coverImage: ['url'] }], - }], - }], - }, ], + withPosts: { + fields: ['slug', 'title', 'publishedAt', { coverImage: ['url'] }], + }, }), schema: z.object({ slug: z.string(), diff --git a/src/hashnode/index.ts b/src/hashnode/index.ts index 8b33d2e..75c7906 100644 --- a/src/hashnode/index.ts +++ b/src/hashnode/index.ts @@ -2,20 +2,39 @@ import * as gql from 'gql-query-builder' import { GraphQLClient } from 'graphql-request' import type { Loader } from 'astro/loaders' +import type IQueryBuilderOptions from 'gql-query-builder/build/IQueryBuilderOptions' import { flattenEdges } from '../utils/flatten-edges' -export interface HashnodeLoaderOptions { +type QueryBuilderOptionsWithOperation = Omit +export type HashnodeLoaderOptions = QueryBuilderOptionsWithOperation & { operation: 'seriesList' | 'posts' | 'staticPages' endpoint?: string publicationHost: string - fields: Array - variables?: Array + [key: `with${string}`]: QueryBuilderOptionsWithOperation +} +type HashnodeLoaderOptionsWithOperation = HashnodeLoaderOptions & { operation: string } + +function getFieldsWithOperation(fields: HashnodeLoaderOptions): IQueryBuilderOptions[] { + const result = [] + + for (const key in fields) { + if (key.startsWith('with')) { + result.push({ + operation: key.slice(4).toLowerCase(), + variables: fields[key as `with${string}`].variables, + fields: fields[key as `with${string}`].fields, + }) + } + } + + return result } async function fetchAllData( client: GraphQLClient, - { fields, variables, operation }: Pick, + options: HashnodeLoaderOptionsWithOperation, ): Promise { + const { fields, variables = [], operation, publicationHost } = options const publication = 'publication' let data: object[] = [] @@ -24,8 +43,7 @@ async function fetchAllData( // Loop through all pages of data while (hasNextPage) { - // build the query - const query = gql.query({ + const queryOptions = { operation: publication, fields: [{ operation, @@ -48,17 +66,43 @@ async function fetchAllData( }, { edges: [ { - node: ['id', ...fields], + node: [ + 'id', + ...fields ?? [], + ...getFieldsWithOperation(options).map(({ operation, fields, variables }) => { + return { + operation, + variables: { + first: { + name: 'first', + required: true, + value: 20, + type: 'Int', + }, + ...variables, + }, + fields: [{ + edges: [{ + node: [...(fields ?? [])], + }], + }], + } + }), + ], }, ], }], }], variables: { ...variables, + host: publicationHost, // if endCursor exists, add it to the variables otherwise, don't ...(endCursor && { after: endCursor }), }, - }) + } + + // build the query + const query = gql.query(queryOptions) // make the request with query and variables const res = await client.request<{ @@ -84,7 +128,8 @@ async function fetchAllData( return data } -export function HashnodeLoader({ endpoint = 'https://gql.hashnode.com', fields, variables = [], publicationHost, operation }: HashnodeLoaderOptions): Loader { +export function HashnodeLoader(options: HashnodeLoaderOptions): Loader { + const { endpoint = 'https://gql.hashnode.com', fields, operation } = options if (!endpoint) { throw new Error('Hashnode requires an endpoint') } @@ -95,18 +140,13 @@ export function HashnodeLoader({ endpoint = 'https://gql.hashnode.com', fields, const client = new GraphQLClient(endpoint) - const connectionVariables = { - ...variables, - host: publicationHost, - } - return { name: 'astro-loader-hashnode', load: async ({ logger, store, parseData }) => { logger.info(`Loading ${operation} data from Hashnode`) store.clear() - const data = await fetchAllData(client, { fields, variables: connectionVariables, operation }) + const data = await fetchAllData(client, options) for (const item of data) { const parsedData = await parseData({ id: item, data: item })