Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions scripts/data/sponsors.mjs → api/sponsors.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
// Builds the data file consumed by the Sponsors layout (`#theme/sponsors`)

import { mkdir, writeFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { fetchWithRetry } from '../utils/fetch.mjs';

const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
const OUTPUT = join(ROOT, 'generated', 'sponsors.json');
import { getCache } from '@vercel/functions';
import { fetchWithRetry } from '../scripts/utils/fetch.mjs';

const COLLECTIVE = 'webpack';
const API = 'https://api.opencollective.com/graphql/v2';
Expand Down Expand Up @@ -102,7 +95,7 @@ const fetchAllOrders = async () => {
return all;
};

const fromApi = async () => {
export const getOpenCollectiveSponsors = async () => {
const nodes = await fetchAllOrders();
if (!nodes.length) throw new Error('No orders returned');

Expand Down Expand Up @@ -169,7 +162,18 @@ const fromApi = async () => {
return { sponsors, backers };
};

const data = await fromApi();
await mkdir(dirname(OUTPUT), { recursive: true });
await writeFile(OUTPUT, `${JSON.stringify(data, null, 2)}\n`);
console.log(`[sponsors] wrote ${OUTPUT}`);
export async function GET() {
const cache = getCache();

// https://vercel.com/docs/caching/runtime-cache?framework=other#using-runtime-cache
let value = await cache.get('sponsors');

if (!value) {
value = await getOpenCollectiveSponsors();
await cache.set('sponsors', value, {
ttl: 3600, // 1 hour in seconds
});
}

return Response.json(value);
}
8 changes: 6 additions & 2 deletions components/HomePage/HomeSponsorSection/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import BaseButton from '@node-core/ui-components/Common/BaseButton';

import SectionHeader from '../../SectionHeader/index.jsx';
import SponsorTier from '../../Sponsors/Tier/index.jsx';
import data from '#theme/sponsors' with { type: 'json' };

import styles from './index.module.css';
import BackerWall from '../../Sponsors/BackerWall/index.jsx';
import useSponsors from '../../../hooks/useSponsors.mjs';

import {
TIERS as BASE_TIERS,
Expand Down Expand Up @@ -44,7 +44,11 @@ function SeeMore({ count, href, className }) {
}

export default () => {
const buckets = useMemo(() => bucketSponsors(data.sponsors, METRIC), []);
const data = useSponsors();
const buckets = useMemo(
() => bucketSponsors(data.sponsors, METRIC),
[data.sponsors]
);
const hasAnySponsor = TIERS.some(({ tier }) => buckets[tier].length > 0);

if (!hasAnySponsor) return null;
Expand Down
14 changes: 6 additions & 8 deletions components/MetaBar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,20 @@ import MetaBar from '@node-core/ui-components/Containers/MetaBar';
import AvatarGroup from '@node-core/ui-components/Common/AvatarGroup';
import BaseButton from '@node-core/ui-components/Common/BaseButton';
import GitHubIcon from '@node-core/ui-components/Icons/Social/GitHub';

import useSponsors from '../../hooks/useSponsors.mjs';
import { editURL } from '#theme/config';
import sponsors from '#theme/sponsors' with { type: 'json' };
import SponsorCard from '../Sponsors/Card/index.jsx';

import styles from './index.module.css';

const OC_URL = 'https://opencollective.com/webpack';

// Active recurring platinum-tier sponsors, ranked by monthly amount. There are
// only ever a handful, so the MetaBar features them as full expanded cards.
const platinumSponsors = sponsors.sponsors
.filter(sponsor => sponsor.monthly.tier === 'platinum')
.sort((a, b) => b.monthly.value - a.monthly.value);

export default ({ metadata, headings = [], readingTime }) => {
const { sponsors } = useSponsors();
const platinumSponsors = sponsors
.filter(sponsor => sponsor.monthly.tier === 'platinum')
.sort((a, b) => b.monthly.value - a.monthly.value);

const editThisPage =
metadata.source ?? editURL.replace('{path}', metadata.path);
const authors = metadata.authors?.split(',').map(id => ({
Expand Down
35 changes: 35 additions & 0 deletions hooks/useSponsors.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useEffect, useState } from 'react';

const EMPTY_DATA = { sponsors: [], backers: [] };

/** @returns {Awaited<ReturnType<typeof import('../api/sponsors.mjs').getOpenCollectiveSponsors>>} */
export default function useSponsors() {
const [data, setData] = useState(EMPTY_DATA);

useEffect(() => {
const controller = new AbortController();

const loadSponsors = async () => {
try {
const response = await fetch('/api/sponsors', {
signal: controller.signal,
});
if (!response.ok) {
throw new Error(`Sponsor API responded ${response.status}`);
}

const value = await response.json();
if (!controller.signal.aborted) setData(value);
} catch (error) {
if (!controller.signal.aborted) {
console.error('Failed to load sponsors', error);
}
}
};

loadSponsors();
return () => controller.abort();
}, []);

return data;
}
3 changes: 2 additions & 1 deletion layouts/Sponsors/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SectionHeader from '../../components/SectionHeader/index.jsx';
import SponsorTier from '../../components/Sponsors/Tier/index.jsx';
import BackerWall from '../../components/Sponsors/BackerWall/index.jsx';
import SortToggle from '../../components/Sponsors/SortToggle/index.jsx';
import data from '#theme/sponsors' with { type: 'json' };
import useSponsors from '../../hooks/useSponsors.mjs';

import styles from './index.module.css';

Expand Down Expand Up @@ -65,6 +65,7 @@ export const bucketSponsors = (sponsors, metric) => {
*/
export default function SponsorsLayout({ metadata }) {
const [metric, setMetric] = useState('monthly');
const data = useSponsors();

const buckets = bucketSponsors(data.sponsors, metric);

Expand Down
Loading
Loading