Skip to content
Open
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
30 changes: 23 additions & 7 deletions apps/frontend/astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,29 @@ export default defineConfig({
}),
],
vite: {
plugins: [tailwindcss()],
plugins: [
tailwindcss(),

// The e2e run sets `STUB_CARD_API`: answer the card endpoints here rather than
// proxy them to a `pnpm dev:backend` that is not running.
!!process.env.STUB_CARD_API && {
name: "stub-card-api",
// In `configureServer`'s body, so it runs ahead of Vite's proxy middleware.
configureServer(server) {
server.middlewares.use((req, res, next) => {
const pathname = req.url?.split("?")[0] ?? "";
if (pathname !== "/api" && !pathname.startsWith("/api/")) {
next();
return;
}
res.setHeader("Content-Type", "image/svg+xml");
res.end(
'<svg xmlns="http://www.w3.org/2000/svg" width="495" height="195"></svg>',
);
});
},
},
],
/*
* On Vercel the same deployment serves `/api` and `/frontend`, so the docs can reference cards by root-relative path.
* Locally the two are separate servers, so forward `/api` to `pnpm dev:backend` to keep those paths working.
Expand All @@ -116,11 +138,5 @@ export default defineConfig({
},
],
},
// The backend code the wizard reuses imports `pg`, which never runs in the browser.
build: {
rolldownOptions: {
external: ["pg"],
},
},
},
});
1 change: 0 additions & 1 deletion apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"@astrojs/react": "^6.0.2",
"@astrojs/starlight": "^0.41.7",
"@reduxjs/toolkit": "^2.12.0",
"@stats-organization/github-readme-stats-backend": "workspace:^",
"@stats-organization/github-readme-stats-core": "workspace:^",
"@tailwindcss/vite": "^4.3.3",
"astro": "^7.2.0",
Expand Down
9 changes: 7 additions & 2 deletions apps/frontend/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ export default defineConfig({
command: "pnpm run dev",
url: baseURL,
reuseExistingServer: !process.env["CI"],
// `astro dev` detaches itself in an AI-agent shell, which reads as exiting early.
env: { ASTRO_DEV_BACKGROUND: "1" },
env: {
// `astro dev` detaches itself in an AI-agent shell, which reads as exiting early.
ASTRO_DEV_BACKGROUND: "1",
// `STUB_CARD_API` has the dev server answer `/api` itself, instead of proxying
// the card endpoints to a backend that is not running here.
STUB_CARD_API: "1",
},
},
});
26 changes: 6 additions & 20 deletions apps/frontend/src/wizard/components/Card/SvgInline.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-expect-error type info should be added later
import { router } from "@stats-organization/github-readme-stats-backend";
import { loadConfigFromEnv } from "@stats-organization/github-readme-stats-core";
import axios from "axios";
import { useEffect, useRef, useState } from "react";
Expand All @@ -12,7 +10,7 @@ import {
useIsAuthenticated,
useUserToken,
} from "../../../redux/selectors/userSelectors.js";
import { createMockRequest, createMockResponse } from "../../mock-http.js";
import { renderCard } from "../../renderCard.js";

interface SvgInlineProps {
url: string;
Expand Down Expand Up @@ -56,7 +54,6 @@ export function SvgInline(props: SvgInlineProps): JSX.Element {
setLoaded(false);

let body: string;
let status;

if (isAuthenticated && (!userToken || userToken === "placeholderPAT")) {
// waiting for backend call to private-access
Expand All @@ -65,24 +62,13 @@ export function SvgInline(props: SvgInlineProps): JSX.Element {

if (stage === 4 && !isAuthenticated) {
const res = await axios.get<string>(url);
if (res.status >= 300) {
console.error("failed to fetch SVG");
return;
}
body = res.data;
status = res.status;
} else {
const req = createMockRequest({
method: "GET",
url,
});
const res = createMockResponse();
// will be solved by npm package
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
await router(req, res);
body = res._getBody() as string;
status = res._getStatusCode();
}

if (status >= 300) {
console.error("failed to fetch/generate SVG");
return;
body = (await renderCard(url)).content;
}

if (!isCurrent) {
Expand Down
96 changes: 0 additions & 96 deletions apps/frontend/src/wizard/mock-http.ts

This file was deleted.

46 changes: 46 additions & 0 deletions apps/frontend/src/wizard/renderCard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {
api,
gist,
pin,
topLangs,
wakatime,
} from "@stats-organization/github-readme-stats-core";

/**
* What a core api handler returns:
* a rendered card, or a rendered error card.
*/
interface CardResult {
status: string;
content: string;
}

/** Core's api handlers are still JavaScript, so every param they destructure is inferred as required. */
type CardHandler = (query: Record<string, string>) => Promise<CardResult>;

const CARD_HANDLERS: Record<string, CardHandler | undefined> = {
"/api": api as CardHandler,
"/api/gist": gist as CardHandler,
"/api/pin": pin as CardHandler,
"/api/top-langs": topLangs as CardHandler,
"/api/wakatime": wakatime as CardHandler,
};

/**
* Renders a card in the browser from the URL the wizard built for it.
* The wizard's PAT arrives through `loadConfigFromEnv` instead.
*
* @param url - Absolute card URL, e.g. `https://host/api/top-langs?username=x`.
* @returns The rendered card, or the rendered error card when a param is rejected.
* @throws When `url` is not one of the card endpoints.
*/
export async function renderCard(url: string): Promise<CardResult> {
const { pathname, searchParams } = new URL(url);

const handler = CARD_HANDLERS[pathname];
if (!handler) {
throw new Error(`No card renderer for "${pathname}"`);
}

return handler(Object.fromEntries(searchParams));
}
3 changes: 0 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.