From 1b7b7da18f7a5e19d9c3a829df10c1737568c949 Mon Sep 17 00:00:00 2001
From: Ryan Christian <33403762+rschristian@users.noreply.github.com>
Date: Mon, 18 Nov 2024 01:38:30 -0600
Subject: [PATCH] refactor: Use prerender data API (#1192)
* refactor: Use prerender data API
* refactor: Switch to proper prerender data provider
* docs: Fix home page github stars repl example
---
content/de/index.md | 2 +-
content/en/index.md | 2 +-
content/es/index.md | 2 +-
content/fr/index.md | 2 +-
content/it/index.md | 2 +-
content/ja/index.md | 2 +-
content/kr/index.md | 2 +-
content/pt-br/index.md | 2 +-
content/ru/index.md | 2 +-
content/tr/index.md | 2 +-
content/zh/index.md | 2 +-
package-lock.json | 24 ++++--
package.json | 2 +-
src/components/app.jsx | 12 ++-
src/components/controllers/tutorial/index.jsx | 8 +-
src/components/github-stars.jsx | 83 ++++++++-----------
src/components/header/gh-version.jsx | 8 +-
src/index.jsx | 28 +++++--
src/lib/github.js | 21 +++--
src/lib/prerender-data.jsx | 72 ++++++++--------
src/types.d.ts | 9 +-
21 files changed, 155 insertions(+), 134 deletions(-)
diff --git a/content/de/index.md b/content/de/index.md
index f19a4057c..02591980c 100755
--- a/content/de/index.md
+++ b/content/de/index.md
@@ -204,7 +204,7 @@ export default class Stars extends Component {
}
}
// --repl-after
-render(<Stars />, document.getElementById("app"));
+render(<Stars repo="preactjs/preact" />, document.getElementById("app"));
diff --git a/content/en/index.md b/content/en/index.md
index 0b0f64b1f..a3339db07 100755
--- a/content/en/index.md
+++ b/content/en/index.md
@@ -200,7 +200,7 @@ export default class Stars extends Component {
}
}
// --repl-after
-render(<Stars />, document.getElementById("app"));
+render(<Stars repo="preactjs/preact" />, document.getElementById("app"));
diff --git a/content/es/index.md b/content/es/index.md
index 9045f3558..b5239729c 100755
--- a/content/es/index.md
+++ b/content/es/index.md
@@ -210,7 +210,7 @@ export default class Stars extends Component {
}
}
// --repl-after
-render(<Stars />, document.getElementById("app"));
+render(<Stars repo="preactjs/preact" />, document.getElementById("app"));
diff --git a/content/fr/index.md b/content/fr/index.md
index d9bdd34ee..b2ab43829 100755
--- a/content/fr/index.md
+++ b/content/fr/index.md
@@ -203,7 +203,7 @@ export default class Stars extends Component {
}
}
// --repl-after
-render(<Stars />, document.getElementById("app"));
+render(<Stars repo="preactjs/preact" />, document.getElementById("app"));
diff --git a/content/it/index.md b/content/it/index.md
index a66610792..9ff950bcb 100755
--- a/content/it/index.md
+++ b/content/it/index.md
@@ -210,7 +210,7 @@ export default class Stars extends Component {
}
}
// --repl-after
-render(<Stars />, document.getElementById("app"));
+render(<Stars repo="preactjs/preact" />, document.getElementById("app"));
diff --git a/content/ja/index.md b/content/ja/index.md
index 2517beba9..f376f33d5 100755
--- a/content/ja/index.md
+++ b/content/ja/index.md
@@ -199,7 +199,7 @@ export default class Stars extends Component {
}
}
// --repl-after
-render(<Stars />, document.getElementById("app"));
+render(<Stars repo="preactjs/preact" />, document.getElementById("app"));
diff --git a/content/kr/index.md b/content/kr/index.md
index bad34726a..b0855875c 100644
--- a/content/kr/index.md
+++ b/content/kr/index.md
@@ -195,7 +195,7 @@ export default class Stars extends Component {
}
}
// --repl-after
-render(<Stars />, document.getElementById("app"));
+render(<Stars repo="preactjs/preact" />, document.getElementById("app"));
diff --git a/content/pt-br/index.md b/content/pt-br/index.md
index bb143c50b..2549fa3b3 100755
--- a/content/pt-br/index.md
+++ b/content/pt-br/index.md
@@ -200,7 +200,7 @@ export default class Stars extends Component {
}
}
// --repl-after
-render(<Stars />, document.getElementById("app"));
+render(<Stars repo="preactjs/preact" />, document.getElementById("app"));
diff --git a/content/ru/index.md b/content/ru/index.md
index 6f92d421f..5f9087ff0 100644
--- a/content/ru/index.md
+++ b/content/ru/index.md
@@ -200,7 +200,7 @@ export default class Stars extends Component {
}
}
// --repl-after
-render(<Stars />, document.getElementById("app"));
+render(<Stars repo="preactjs/preact" />, document.getElementById("app"));
diff --git a/content/tr/index.md b/content/tr/index.md
index 702ede75c..4ea40aabe 100755
--- a/content/tr/index.md
+++ b/content/tr/index.md
@@ -212,7 +212,7 @@ export default class Stars extends Component {
}
}
// --repl-after
-render(<Stars />, document.getElementById("app"));
+render(<Stars repo="preactjs/preact" />, document.getElementById("app"));
diff --git a/content/zh/index.md b/content/zh/index.md
index afafef23d..141e7887b 100755
--- a/content/zh/index.md
+++ b/content/zh/index.md
@@ -203,7 +203,7 @@ export default class Stars extends Component {
}
}
// --repl-after
-render(<Stars />, document.getElementById("app"));
+render(<Stars repo="preactjs/preact" />, document.getElementById("app"));
diff --git a/package-lock.json b/package-lock.json
index 55a31760c..149dec785 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,7 @@
"@codemirror/state": "^6.4.1",
"@codemirror/view": "^6.28.1",
"@docsearch/react": "^3.6.0",
- "@preact/preset-vite": "^2.8.3",
+ "@preact/preset-vite": "^2.9.1",
"@preact/signals": "^1.1.3",
"@preact/signals-core": "^1.2.3",
"@rollup/browser": "^3.18.0",
@@ -1512,9 +1512,9 @@
}
},
"node_modules/@preact/preset-vite": {
- "version": "2.8.3",
- "resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.8.3.tgz",
- "integrity": "sha512-IT4+IV3D4lIyoDrp4FUfx4dT2yW/5KIl2MXAsqqItGTbz1xUhtSyP9nS2kLXNnhLG4I2Nku/X+vKFMmRG+oMDA==",
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.9.1.tgz",
+ "integrity": "sha512-JecWzrOx7ogFhklSMhY+aH/24pajL0Vx+beEgau3WDMUUAo32cpUo/UqerPhLOyhCKXlxK9a3cRoa8g68ZAp5g==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.22.13",
@@ -1527,7 +1527,6 @@
"kolorist": "^1.8.0",
"magic-string": "0.30.5",
"node-html-parser": "^6.1.10",
- "resolve": "^1.22.8",
"source-map": "^0.7.4",
"stack-trace": "^1.0.0-pre2"
},
@@ -3998,6 +3997,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -4294,6 +4294,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+ "dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -4521,6 +4522,7 @@
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
+ "dev": true,
"dependencies": {
"hasown": "^2.0.0"
},
@@ -5773,7 +5775,8 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
},
"node_modules/path-scurry": {
"version": "1.10.1",
@@ -5874,9 +5877,10 @@
}
},
"node_modules/preact-iso": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/preact-iso/-/preact-iso-2.6.3.tgz",
- "integrity": "sha512-2JqNi+Elyt7rf0kULtMn/QskbZE10cDMrhmoanGKWJX3gOCTIfLt/ta5xWu+kQUsyInPkTQIVp3fS5Im0V+X8A==",
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/preact-iso/-/preact-iso-2.8.1.tgz",
+ "integrity": "sha512-RL0yDdJcSiB4Wnk4fBqq0OE9QCfTEW7B2ULf8GHPx72AtFE+j1NvabjBF6y3rBgrNWSuuPuuiN4L4KxYteQD2Q==",
+ "license": "MIT",
"peerDependencies": {
"preact": ">=10",
"preact-render-to-string": ">=6.4.0"
@@ -6016,6 +6020,7 @@
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dev": true,
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
@@ -6580,6 +6585,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
"engines": {
"node": ">= 0.4"
},
diff --git a/package.json b/package.json
index def8e43c3..56628fac3 100644
--- a/package.json
+++ b/package.json
@@ -75,7 +75,7 @@
"@codemirror/state": "^6.4.1",
"@codemirror/view": "^6.28.1",
"@docsearch/react": "^3.6.0",
- "@preact/preset-vite": "^2.8.3",
+ "@preact/preset-vite": "^2.9.1",
"@preact/signals": "^1.1.3",
"@preact/signals-core": "^1.2.3",
"@rollup/browser": "^3.18.0",
diff --git a/src/components/app.jsx b/src/components/app.jsx
index 46ace8b87..434a62e05 100644
--- a/src/components/app.jsx
+++ b/src/components/app.jsx
@@ -1,15 +1,21 @@
import { LocationProvider, ErrorBoundary } from 'preact-iso';
import { LanguageProvider } from '../lib/i18n';
+import { PrerenderDataProvider } from '../lib/prerender-data.jsx';
import Header from './header';
import Routes from './routes';
-export default function App() {
+/**
+ * @param {{ prerenderData?: import('../types.d.ts').PrerenderData }} props
+ */
+export default function App({ prerenderData }) {
return (
-
-
+
+
+
+
diff --git a/src/components/controllers/tutorial/index.jsx b/src/components/controllers/tutorial/index.jsx
index 64b8527b4..38c5f84b1 100644
--- a/src/components/controllers/tutorial/index.jsx
+++ b/src/components/controllers/tutorial/index.jsx
@@ -8,12 +8,11 @@ import {
useMemo,
useCallback
} from 'preact/hooks';
-import { useLocation, useRoute } from 'preact-iso';
+import { useLocation } from 'preact-iso';
import { TutorialContext, SolutionContext } from './contexts';
import { ErrorOverlay } from '../repl/error-overlay';
import { parseStackTrace } from '../repl/errors';
import cx from '../../../lib/cx';
-import { InjectPrerenderData } from '../../../lib/prerender-data';
import { useResource } from '../../../lib/use-resource';
import { useLanguage } from '../../../lib/i18n';
import { Splitter } from '../../splitter';
@@ -220,11 +219,6 @@ export function Tutorial({ html, meta }) {
-
-
);
diff --git a/src/components/github-stars.jsx b/src/components/github-stars.jsx
index 030df2342..6ba3c05b1 100644
--- a/src/components/github-stars.jsx
+++ b/src/components/github-stars.jsx
@@ -1,6 +1,6 @@
-import { Component } from 'preact';
-import { localStorageGet, localStorageSet } from '../lib/localstorage';
-import { fallbackData, repoInfo } from '../lib/github';
+import { useEffect, useState } from 'preact/hooks';
+import { repoInfo } from '../lib/github';
+import { usePrerenderData } from '../lib/prerender-data.jsx';
const githubStars = repo =>
repoInfo(repo).then(d => d.stargazers_count);
@@ -10,53 +10,42 @@ const formatNumber = num => (num + '').replace(/(\d{3})$/g, ',$1');
// make available to homepage REPL demo
if (typeof window !== 'undefined') window.githubStars = githubStars;
-export default class GithubStars extends Component {
- state = {
- stars: localStorageGet('_stars') || fallbackData.preactStargazers
- };
+export default function GitHubStars({ user, repo, simple, children }) {
+ const { preactStargazers } = usePrerenderData();
+ const [stars, setStars] = useState(preactStargazers);
- setStars = stars => {
- if (stars && stars != this.state.stars) {
- localStorageSet('_stars', stars);
- this.setState({ stars });
- }
- };
-
- componentDidMount() {
- let { user, repo } = this.props;
- githubStars(user + '/' + repo).then(this.setStars);
- }
-
- render({ user, repo, simple, children }, { stars }) {
- let url = `https://github.com/${user}/${repo}/`;
- if (simple) {
- return (
-
- ⭐️ {stars} Stars
-
- );
- }
+ useEffect(() => {
+ githubStars(`${user}/${repo}`).then(setStars);
+ }, []);
+ const url = `https://github.com/${user}/${repo}/`;
+ if (simple) {
return (
-
-
- Star
-
-
- {stars ? formatNumber(Math.round(stars)) : children || '..'}
-
-
+
+ ⭐️ {stars} Stars
+
);
}
+
+ return (
+
+
+ Star
+
+
+ {stars ? formatNumber(Math.round(stars)) : children || '..'}
+
+
+ );
}
diff --git a/src/components/header/gh-version.jsx b/src/components/header/gh-version.jsx
index 74ca45b49..81aa3d2cd 100644
--- a/src/components/header/gh-version.jsx
+++ b/src/components/header/gh-version.jsx
@@ -1,11 +1,13 @@
import { useEffect, useState } from 'preact/hooks';
-import { fallbackData, fetchRelease } from '../../lib/github';
+import { fetchRelease } from '../../lib/github';
+import { usePrerenderData } from '../../lib/prerender-data.jsx';
import config from '../../config.json';
export default function ReleaseLink({ ...props }) {
+ const { preactReleaseURL, preactVersion } = usePrerenderData();
const [release, setRelease] = useState({
- url: globalThis.prerenderPreactReleaseUrl || fallbackData.preactReleaseUrl,
- version: globalThis.prerenderPreactVersion || fallbackData.preactVersion
+ url: preactReleaseURL,
+ version: preactVersion
});
useEffect(() => {
diff --git a/src/index.jsx b/src/index.jsx
index 376a99086..9fa8e1b11 100755
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -23,7 +23,12 @@ if (typeof window !== 'undefined') {
}
}
-let initialized = false;
+let initialized = false,
+ prerenderData = {
+ preactVersion: '',
+ preactReleaseURL: '',
+ preactStargazers: 0
+ };
export async function prerender() {
const init = async () => {
globalThis.prismWorker = await import('./components/code-block/prism.worker.js');
@@ -35,14 +40,19 @@ export async function prerender() {
// fetch latest release data
const { default: releaseLambda } = await import('./lambda/release.js');
const { version, url } = await (await releaseLambda()).json();
- globalThis.prerenderPreactVersion = version;
- globalThis.prerenderPreactReleaseUrl = url;
+ prerenderData.preactVersion = version;
+ prerenderData.preactReleaseURL = url;
+
+ // fetch latest stargazer count
+ const { default: repoLambda } = await import('./lambda/repo.js');
+ const { stargazers_count: stargazersCount } = await (await repoLambda()).json();
+ prerenderData.preactStargazers = stargazersCount;
initialized = true;
};
if (!initialized) await init();
- const res = await ssr();
+ const res = await ssr();
const elements = new Set([
{ type: 'meta', props: { name: 'description', content: globalThis.description } },
@@ -55,5 +65,13 @@ export async function prerender() {
res.html += '';
- return { ...res, head: { lang: 'en', title: globalThis.title, elements } };
+ return {
+ ...res,
+ data: prerenderData,
+ head: {
+ lang: 'en',
+ title: globalThis.title,
+ elements
+ }
+ };
}
diff --git a/src/lib/github.js b/src/lib/github.js
index fa3035936..56eb56cd6 100644
--- a/src/lib/github.js
+++ b/src/lib/github.js
@@ -1,3 +1,5 @@
+import { getFallbackData } from './prerender-data.jsx';
+
/**
* Throw if the response status is in the error range
* @param {Response} r
@@ -9,12 +11,6 @@ function checkStatus(r) {
return r;
}
-export const fallbackData = {
- preactVersion: '10.19.5',
- preactReleaseUrl: 'https://github.com/preactjs/preact/releases/tag/10.19.5',
- preactStargazers: 35783
-};
-
const baseUrl = '/.netlify/functions/';
export const repoInfo = repo =>
@@ -22,14 +18,17 @@ export const repoInfo = repo =>
.then(checkStatus)
.then(r => r.json())
.catch(() => ({
- stargazers_count: fallbackData.preactStargazers
+ stargazers_count: getFallbackData().preactStargazers
}));
export const fetchRelease = repo =>
fetch(`${baseUrl}release?repo=${repo}`, { credentials: 'omit' })
.then(checkStatus)
.then(r => r.json())
- .catch(() => ({
- version: fallbackData.preactVersion,
- url: fallbackData.preactReleaseUrl
- }));
+ .catch(() => {
+ const fallbackData = getFallbackData();
+ return {
+ version: fallbackData.preactVersion,
+ url: fallbackData.preactReleaseUrl
+ };
+ });
diff --git a/src/lib/prerender-data.jsx b/src/lib/prerender-data.jsx
index 7d74e6269..7e58b7b6a 100644
--- a/src/lib/prerender-data.jsx
+++ b/src/lib/prerender-data.jsx
@@ -1,40 +1,46 @@
-const TYPE = 'text/pd';
+import { createContext } from 'preact';
+import { useContext } from 'preact/hooks';
-const prerenderNodes = typeof window !== 'undefined' && document.querySelectorAll(`[type="${TYPE}"]`);
-const prerenderData = {};
+/**
+ * @typedef {import('../types.d.ts').PrerenderData} PrerenderData
+ */
-export function getPrerenderData(name) {
- if (!prerenderNodes) return;
- if (name in prerenderData) return prerenderData[name];
- for (let i = 0; i < prerenderNodes.length; i++) {
- if (prerenderNodes[i].getAttribute('data-pd') === name) {
- let data;
- try {
- data = JSON.parse(
- prerenderNodes[i].firstChild.data.replace(
- /<_(_*)\/script>/g,
- '<$1/script>'
- )
- );
- } catch (e) {}
- return (prerenderData[name] = data);
- }
- }
+/**
+ * @returns {PrerenderData | {}}
+ */
+export function getFallbackData() {
+ if (typeof window === 'undefined') return {};
+ const el = document.getElementById('preact-prerender-data');
+ if (!el) return {};
+ const data = JSON.parse(el.textContent);
+ return data;
}
-export function InjectPrerenderData({ name, data }) {
- if (typeof window === 'undefined') return null;
- const content = JSON.stringify(data).replace(
- /<(_*)\/script>/g,
- '<$1_/script>'
- );
+/**
+ * @type {import('preact').Context}
+ */
+const PrerenderDataContext = createContext(/** @type {PrerenderData} */ ({}));
+
+/**
+ * @param {{ value?: PrerenderData, children: any }} props
+ */
+export function PrerenderDataProvider({ value, children }) {
+ const fallbackData = getFallbackData();
+
+ const preactVersion = value?.preactVersion || fallbackData.preactVersion;
+ const preactReleaseURL = value?.preactReleaseURL || fallbackData.preactReleaseUrl;
+ const preactStargazers = value?.preactStargazers || fallbackData.preactStargazers;
+
return (
-
+
+ {children}
+
);
}
+
+/**
+ * @returns {PrerenderData}
+ */
+export function usePrerenderData() {
+ return useContext(PrerenderDataContext);
+}
diff --git a/src/types.d.ts b/src/types.d.ts
index 417d75548..ba1930234 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -7,9 +7,10 @@ declare global {
'content-region': { name: string; 'data-page-nav': boolean; 'can-edit': boolean, children: any };
}
}
- var prerenderPreactVersion: string;
- var prerenderPreactReleaseUrl: string;
}
-
-export {}
+export interface PrerenderData {
+ preactVersion: string;
+ preactReleaseURL: string;
+ preactStargazers: number;
+}