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 ( -