diff --git a/.gitignore b/.gitignore index 3013a2ce97..70e68c19a1 100644 --- a/.gitignore +++ b/.gitignore @@ -127,7 +127,6 @@ packages/web/app/next.config.mjs packages/web/app/environment-*.mjs packages/web/app/src/gql/*.ts packages/web/app/src/gql/*.json -packages/web/docs/public/feed.xml # Changelog packages/web/app/src/components/ui/changelog/generated-changelog.ts diff --git a/packages/web/docs/next-env.d.ts b/packages/web/docs/next-env.d.ts index 4f11a03dc6..725dd6f245 100644 --- a/packages/web/docs/next-env.d.ts +++ b/packages/web/docs/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/packages/web/docs/next-sitemap.config.cjs b/packages/web/docs/next-sitemap.config.js similarity index 71% rename from packages/web/docs/next-sitemap.config.cjs rename to packages/web/docs/next-sitemap.config.js index af0315e2f8..901d52bbf1 100644 --- a/packages/web/docs/next-sitemap.config.cjs +++ b/packages/web/docs/next-sitemap.config.js @@ -1,7 +1,5 @@ -/* eslint-disable no-undef, no-process-env */ /** @type {import('next-sitemap').IConfig} */ - -module.exports = { +export default { siteUrl: process.env.SITE_URL || 'https://graphql-hive.com', generateIndexSitemap: false, output: 'export', diff --git a/packages/web/docs/next.config.mjs b/packages/web/docs/next.config.js similarity index 98% rename from packages/web/docs/next.config.mjs rename to packages/web/docs/next.config.js index 950340e02b..aea25b5941 100644 --- a/packages/web/docs/next.config.mjs +++ b/packages/web/docs/next.config.js @@ -1,8 +1,9 @@ -/* eslint-disable no-process-env */ - import { withGuildDocs } from '@theguild/components/next.config'; export default withGuildDocs({ + nextraConfig: { + themeConfig: './src/theme.config.tsx', + }, output: 'export', basePath: process.env.NEXT_BASE_PATH, eslint: { diff --git a/packages/web/docs/package.json b/packages/web/docs/package.json index 537672bf41..8add68682c 100644 --- a/packages/web/docs/package.json +++ b/packages/web/docs/package.json @@ -3,12 +3,10 @@ "type": "module", "private": true, "scripts": { - "build": "pnpm run generate-rss && next build && next-sitemap --config next-sitemap.config.cjs", - "dev": "next", - "generate-rss": "tsx ./scripts/rss-generator.ts" + "build": "next build && next-sitemap", + "dev": "next" }, "dependencies": { - "@next/env": "14.2.6", "@radix-ui/react-accordion": "1.2.0", "@radix-ui/react-icons": "1.3.0", "@radix-ui/react-tabs": "1.1.0", @@ -16,16 +14,12 @@ "@theguild/components": "7.0.0-alpha-20240910155635-d57d888ceb50ef15da6fbc43f2eab9a74fe5028f", "clsx": "2.1.1", "date-fns": "3.6.0", - "gray-matter": "4.0.3", "next": "14.2.10", - "next-sitemap": "4.2.3", - "next-themes": "*", "react": "18.3.1", "react-avatar": "5.0.3", "react-countup": "6.5.3", "react-dom": "18.3.1", "react-icons": "5.3.0", - "rss": "1.2.2", "tailwind-merge": "2.5.2", "tailwindcss-animate": "1.0.7", "tailwindcss-radix": "3.0.3" @@ -33,8 +27,10 @@ "devDependencies": { "@theguild/tailwind-config": "0.5.0", "@types/react": "18.3.3", + "@types/rss": "^0.0.32", "next-sitemap": "4.2.3", "postcss": "8.4.41", + "rss": "1.2.2", "tailwindcss": "3.4.10" } } diff --git a/packages/web/docs/scripts/rss-generator.ts b/packages/web/docs/scripts/rss-generator.ts deleted file mode 100644 index e76eeb07c9..0000000000 --- a/packages/web/docs/scripts/rss-generator.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { promises as fs } from 'fs'; -import path from 'path'; -import matter from 'gray-matter'; -import RSS from 'rss'; - -type Meta = { - title: string; - date: string; - url: string; - description: string; -}; - -async function generateRSS() { - const __dirname = path.resolve(path.dirname('')); - const feed = new RSS({ - title: 'Hive Changelog', - site_url: 'https://the-guild.dev/graphql/hive', - feed_url: 'https://the-guild.dev/graphql/hive/feed.xml', - }); - - const allChangelogs = await fs.readdir( - path.join(__dirname, '..', 'docs', 'src', 'pages', 'product-updates'), - ); - const allChangelogsPosts = [] as Meta[]; - await Promise.all( - allChangelogs.map(async name => { - if (name.startsWith('index.') || name.startsWith('_meta.') || name.startsWith('_')) return; - - const content = await fs.readFile( - path.join(__dirname, '..', 'docs', 'src', 'pages', 'product-updates', name), - ); - const frontmatter = matter(content); - - allChangelogsPosts.push({ - title: frontmatter.data.title, - date: frontmatter.data.date, - url: `https://the-guild.dev/graphql/hive/product-updates/${name.replace(/\.mdx$/, '')}`, - description: frontmatter.data.description, - }); - }), - ); - - allChangelogsPosts.sort((a, b) => { - return new Date(b.date).getTime() - new Date(a.date).getTime(); - }); - allChangelogsPosts.forEach(post => { - feed.item(post); - }); - await fs.writeFile('./public/feed.xml', feed.xml({ indent: true })); -} - -try { - generateRSS(); - console.log('✅ RSS generated'); -} catch (e) { - console.error(e); - process.exit(1); -} diff --git a/packages/web/docs/src/app/feed.xml/route.ts b/packages/web/docs/src/app/feed.xml/route.ts new file mode 100644 index 0000000000..efd84c1f52 --- /dev/null +++ b/packages/web/docs/src/app/feed.xml/route.ts @@ -0,0 +1,26 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import RSS from 'rss'; +import { getChangelogs } from '../../components/product-updates'; + +export async function GET() { + const feed = new RSS({ + title: 'Hive Changelog', + site_url: 'https://the-guild.dev/graphql/hive', + feed_url: 'https://the-guild.dev/graphql/hive/feed.xml', + }); + + for (const item of await getChangelogs()) { + feed.item({ + title: item.title, + date: item.date, + url: `https://the-guild.dev/graphql/hive${item.route}`, + description: item.description, + }); + } + + return new Response(feed.xml({ indent: true }), { + headers: { + 'Content-Type': 'application/xml; charset=utf-8', + }, + }); +} diff --git a/packages/web/docs/src/authors.ts b/packages/web/docs/src/authors.ts index e99fd67b15..18b076b1b8 100644 --- a/packages/web/docs/src/authors.ts +++ b/packages/web/docs/src/authors.ts @@ -8,12 +8,12 @@ type Author = { export const authors: Record = { kamil: { name: 'Kamil Kisiela', - link: 'https://twitter.com/kamilkisiela', + link: 'https://x.com/kamilkisiela', github: 'kamilkisiela', }, laurin: { name: 'Laurin Quast', - link: 'https://twitter.com/n1rual', + link: 'https://x.com/n1rual', github: 'n1ru4l', }, arda: { @@ -23,12 +23,17 @@ export const authors: Record = { }, aleksandra: { name: 'Aleksandra Sikora', - link: 'https://twitter.com/aleksandrasays', + link: 'https://x.com/aleksandrasays', github: 'beerose', }, jiri: { name: 'Jiri Spac', - link: 'https://twitter.com/capajj', + link: 'https://x.com/capajj', github: 'capaj', }, + dimitri: { + name: 'Dimitri Postolov', + link: 'https://x.com/dimaMachina_', + github: 'dimaMachina', + }, }; diff --git a/packages/web/docs/src/components/product-updates.tsx b/packages/web/docs/src/components/product-updates.tsx index 1778dd1f8f..12455a4a7b 100644 --- a/packages/web/docs/src/components/product-updates.tsx +++ b/packages/web/docs/src/components/product-updates.tsx @@ -1,10 +1,7 @@ -import fs from 'node:fs'; -import path from 'node:path'; import { ReactElement } from 'react'; import type { GetStaticProps } from 'next'; import Link from 'next/link'; import { format } from 'date-fns'; -import matter from 'gray-matter'; type Changelog = { title: string; @@ -13,7 +10,7 @@ type Changelog = { route: string; }; -function ProductUpdateTeaser(props: Changelog) { +export function ProductUpdateTeaser(props: Changelog): ReactElement { return (
  • @@ -33,49 +30,48 @@ function ProductUpdateTeaser(props: Changelog) { ); } -export const ProductUpdates = (props: { changelogs: Changelog[] }): ReactElement => { - return ( - <> -
    -

    Product Updates

    -

    The most recent developments from GraphQL Hive.

    -
    -
      - {props.changelogs.map(item => ( - - ))} -
    - - ); -}; +export async function getChangelogs(): Promise { + const { pageMap } = await import('../../.next/static/chunks/nextra-page-map-.mjs'); -export const getStaticProps: GetStaticProps<{ ssg: { changelogs: Changelog[] } }> = async () => { - const productUpdatesDirectory = path.join(process.cwd(), 'src', 'pages', 'product-updates'); - const filenames = fs.readdirSync(productUpdatesDirectory); - const changelogs: Changelog[] = []; + const productUpdatesFolder = pageMap.find(item => item.route === '/product-updates')!.children!; - for (const filename of filenames) { - if (filename.endsWith('.json') || filename.endsWith('index.mdx') || filename.endsWith('.ts')) { - continue; - } + return productUpdatesFolder + .slice(1) // cut `_meta.ts` which always comes first + .map(item => { + if (!item.children) { + if (!('title' in item.frontMatter!)) { + throw new Error(`Incorrect Front matter on page ${item.route}`); + } - const { data } = matter( - fs.readFileSync(path.join(productUpdatesDirectory, filename), 'utf8'), - {}, - ); + // Regular mdx page + return { + title: item.frontMatter.title, + date: item.frontMatter.date.toISOString(), + description: item.frontMatter.description, + route: item.route!, + }; + } + // Folder + const indexPage = item.children.find(item => item.name === 'index'); + if (!indexPage) { + throw new Error('Changelog folder must have an "index.mdx" page'); + } - if (data.title && data.description && data.date) { - changelogs.push({ - date: data.date.toISOString(), - title: data.title, - description: data.description, - route: `/product-updates/${filename.replace(/\.mdx$/, '')}`, - }); - } - } + if (!('date' in indexPage.frontMatter!)) { + throw new Error(`Incorrect Front matter on page ${item.route}`); + } - changelogs.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + return { + title: indexPage.frontMatter.title, + date: indexPage.frontMatter.date.toISOString(), + description: indexPage.frontMatter.description, + route: indexPage.route!, + }; + }) + .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); +} +export const getStaticProps: GetStaticProps<{ ssg: { changelogs: Changelog[] } }> = async () => { return { props: { __nextra_dynamic_opts: { @@ -84,7 +80,7 @@ export const getStaticProps: GetStaticProps<{ ssg: { changelogs: Changelog[] } } description: 'The most recent developments from GraphQL Hive.', }, }, - ssg: { changelogs }, + ssg: { changelogs: await getChangelogs() }, }, }; }; diff --git a/packages/web/docs/src/pages/_meta.tsx b/packages/web/docs/src/pages/_meta.tsx index 6a5df41cb6..8611c11d92 100644 --- a/packages/web/docs/src/pages/_meta.tsx +++ b/packages/web/docs/src/pages/_meta.tsx @@ -1,6 +1,5 @@ import type { Item, MenuItem, PageItem } from 'nextra/normalize-pages'; -import { PRODUCTS } from '@theguild/components'; -import { SIX_HIGHLIGHTED_PRODUCTS } from '@theguild/components/products'; +import { PRODUCTS, SIX_HIGHLIGHTED_PRODUCTS } from '@theguild/components/products'; import { cn } from '../lib'; const meta: Record> = { diff --git a/packages/web/docs/src/pages/product-updates.mdx b/packages/web/docs/src/pages/product-updates.mdx new file mode 100644 index 0000000000..b67dbba3f8 --- /dev/null +++ b/packages/web/docs/src/pages/product-updates.mdx @@ -0,0 +1,24 @@ +import { useData } from '@theguild/components' +import { ProductUpdateTeaser } from '../components/product-updates' + +export { getStaticProps } from '../components/product-updates' + +export function ProductUpdatesPage() { + const { changelogs } = useData() + return ( +
      + {changelogs.map(item => ( + + ))} +
    + ) +} + +
    + # Product Updates + +The most recent developments from GraphQL Hive. + +
    + + diff --git a/packages/web/docs/src/pages/product-updates/2024-10-11-laboratory-improvements/full-screen-mode.mp4 b/packages/web/docs/src/pages/product-updates/2024-10-11-laboratory-improvements/full-screen-mode.mp4 new file mode 100644 index 0000000000..5264b64ab8 Binary files /dev/null and b/packages/web/docs/src/pages/product-updates/2024-10-11-laboratory-improvements/full-screen-mode.mp4 differ diff --git a/packages/web/docs/src/pages/product-updates/2024-10-11-laboratory-improvements/index.mdx b/packages/web/docs/src/pages/product-updates/2024-10-11-laboratory-improvements/index.mdx new file mode 100644 index 0000000000..0b6052d655 --- /dev/null +++ b/packages/web/docs/src/pages/product-updates/2024-10-11-laboratory-improvements/index.mdx @@ -0,0 +1,73 @@ +--- +title: Laboratory Improvements +description: + The laboratory received a new look, tabs support, Query Builder plugin and uses GraphiQL v4 alpha. +date: 2024-10-11 +authors: [dimitri] +--- + +import fullScreenMode from './full-screen-mode.mp4' +import queryBuilder from './query-builder.mp4' +import newTabs from './tabs-new.mp4' + +export function Caption({ children }) { + return

    {children}

    +} + +export function Video({ src, alt }) { + return ( + <> + + {alt} + + ) +} + +Over the past few months, we've focused on enhancing Hive's laboratory, refining its appearance, and +adding new features. Here's what we've improved. + +## Operations Collections + +### New Look + +Hive's Operations collections received a new look to make consistency with Hive UI, to make it +easier to use and more obvious. GraphiQL toolbars buttons "Copy Query" and "Merge fragments into +query" were removed, "Prettify query" button was moved to the end of the toolbar. + +![New look of Operations collections](./operations-collections-plugin-new.png) + +New look of Operations collections + +### Full Screen Mode + +Users with wide screens can now benefit from the fullscreen mode. The button is located in place of +the GraphiQL logo. + +