Skip to content

Commit 8918904

Browse files
embed tanstack site
1 parent 7ba29d5 commit 8918904

File tree

5 files changed

+426
-23
lines changed

5 files changed

+426
-23
lines changed

packages/website-v2/public/tanstack-router-banner.svg

Lines changed: 119 additions & 0 deletions
Loading

packages/website-v2/public/tanstack-start-banner.svg

Lines changed: 119 additions & 0 deletions
Loading

packages/website-v2/src/marketplace/MarketplacePage.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Link } from "@tanstack/react-router";
22
import { useEffect, useRef, useState } from "react";
33
import type { MarketplaceManifest } from "@inlang/marketplace-manifest";
4+
import { isMarketplaceMirrorPage } from "./marketplaceData";
45
import type {
56
MarketplacePageData,
67
MarketplaceHeading,
@@ -459,7 +460,10 @@ function DocNav({
459460
flatPages.push({
460461
route: key,
461462
path: value,
462-
isExternal: !value.endsWith(".md") && !value.endsWith(".html"),
463+
isExternal:
464+
!value.endsWith(".md") &&
465+
!value.endsWith(".html") &&
466+
!isMarketplaceMirrorPage(manifest.id, key),
463467
});
464468
} else {
465469
// Nested structure - use key as section title (empty string becomes "Overview")
@@ -471,7 +475,8 @@ function DocNav({
471475
path: path as string,
472476
isExternal:
473477
!(path as string).endsWith(".md") &&
474-
!(path as string).endsWith(".html"),
478+
!(path as string).endsWith(".html") &&
479+
!isMarketplaceMirrorPage(manifest.id, route),
475480
})),
476481
});
477482
}
@@ -1014,10 +1019,7 @@ function getGithubLink(
10141019
}
10151020

10161021
if (!link) return undefined;
1017-
return `https://github.com/opral/inlang/blob/main/${link.replace(
1018-
"./",
1019-
"",
1020-
)}`;
1022+
return `https://github.com/opral/inlang/blob/main/${link.replace("./", "")}`;
10211023
}
10221024

10231025
function PageNavigation({
@@ -1040,7 +1042,10 @@ function PageNavigation({
10401042

10411043
for (const [key, value] of entries) {
10421044
if (typeof value === "string") {
1043-
const isExternal = !value.endsWith(".md") && !value.endsWith(".html");
1045+
const isExternal =
1046+
!value.endsWith(".md") &&
1047+
!value.endsWith(".html") &&
1048+
!isMarketplaceMirrorPage(manifest.id, key);
10441049
if (!isExternal) {
10451050
allPages.push({
10461051
route: key,
@@ -1052,7 +1057,10 @@ function PageNavigation({
10521057
for (const [route, path] of Object.entries(
10531058
value as Record<string, string>,
10541059
)) {
1055-
const isExternal = !path.endsWith(".md") && !path.endsWith(".html");
1060+
const isExternal =
1061+
!path.endsWith(".md") &&
1062+
!path.endsWith(".html") &&
1063+
!isMarketplaceMirrorPage(manifest.id, route);
10561064
if (!isExternal) {
10571065
allPages.push({
10581066
route,

packages/website-v2/src/marketplace/marketplaceData.ts

Lines changed: 165 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,84 @@ export type MarketplacePageData = {
3232
nextPagePath?: string;
3333
};
3434

35+
type MarketplaceMirrorSpec = {
36+
rawUrl: string;
37+
sourceUrl: string;
38+
sourceLabel: string;
39+
ogTitle: string;
40+
ogDescription: string;
41+
ogImage: string;
42+
heroImage: string;
43+
heroImageAlt: string;
44+
ciImage: string;
45+
ciImageAlt: string;
46+
pitch: string;
47+
};
48+
49+
const marketplaceMirrors: Record<
50+
string,
51+
Record<string, MarketplaceMirrorSpec>
52+
> = {
53+
"library.inlang.paraglideJs": {
54+
"/tanstack-router": {
55+
rawUrl:
56+
"https://raw.githubusercontent.com/TanStack/router/main/examples/react/i18n-paraglide/README.md",
57+
sourceUrl:
58+
"https://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide",
59+
sourceLabel: "TanStack/router",
60+
ogTitle: "The recommended i18n for TanStack Router",
61+
ogDescription:
62+
"Type-safe i18n with tiny bundles. Fully integrated with TanStack Router and tested in TanStack's CI/CD pipeline.",
63+
ogImage: "https://inlang.com/tanstack-router-banner.svg",
64+
heroImage: "https://inlang.com/tanstack-router-banner.svg",
65+
heroImageAlt: "Paraglide JS for TanStack Router overview",
66+
ciImage: "https://inlang.com/images/tanstack-ci-paraglide.svg",
67+
ciImageAlt: "Paraglide JS tested in TanStack CI/CD",
68+
pitch: [
69+
"- Fully type-safe with IDE autocomplete",
70+
"- SEO-friendly localized URLs",
71+
"- Works with CSR, SSR, and SSG",
72+
"- Tested as part of [TanStack's CI/CD pipeline](https://inlang.com/blog/tanstack-ci)",
73+
].join("\n"),
74+
},
75+
"/tanstack-start": {
76+
rawUrl:
77+
"https://raw.githubusercontent.com/TanStack/router/main/examples/react/start-i18n-paraglide/README.md",
78+
sourceUrl:
79+
"https://github.com/TanStack/router/tree/main/examples/react/start-i18n-paraglide",
80+
sourceLabel: "TanStack/router",
81+
ogTitle: "The recommended i18n for TanStack Start",
82+
ogDescription:
83+
"Type-safe i18n with tiny bundles. Fully integrated with TanStack Start and tested in TanStack's CI/CD pipeline.",
84+
ogImage: "https://inlang.com/tanstack-start-banner.svg",
85+
heroImage: "https://inlang.com/tanstack-start-banner.svg",
86+
heroImageAlt: "Paraglide JS for TanStack Start overview",
87+
ciImage: "https://inlang.com/images/tanstack-ci-paraglide.svg",
88+
ciImageAlt: "Paraglide JS tested in TanStack CI/CD",
89+
pitch: [
90+
"- Fully type-safe with IDE autocomplete",
91+
"- SEO-friendly localized URLs",
92+
"- Works with CSR, SSR, and SSG",
93+
"- Tested as part of [TanStack's CI/CD pipeline](https://inlang.com/blog/tanstack-ci)",
94+
].join("\n"),
95+
},
96+
},
97+
};
98+
99+
export function getMarketplaceMirrorSpec(
100+
manifestId: string,
101+
pagePath: string,
102+
): MarketplaceMirrorSpec | undefined {
103+
return marketplaceMirrors[manifestId]?.[pagePath];
104+
}
105+
106+
export function isMarketplaceMirrorPage(
107+
manifestId: string,
108+
pagePath: string,
109+
): boolean {
110+
return Boolean(getMarketplaceMirrorSpec(manifestId, pagePath));
111+
}
112+
35113
export async function loadMarketplacePage({
36114
uid,
37115
slug,
@@ -99,20 +177,39 @@ export async function loadMarketplacePage({
99177
}
100178

101179
const [, page] = pageEntry;
102-
if (!page || !(await fileExists(page))) {
180+
const mirrorSpec = getMarketplaceMirrorSpec(item.id, pagePath);
181+
if (!page || (!mirrorSpec && !(await fileExists(page)))) {
103182
throw redirect({ to: itemPath });
104183
}
105184

106-
sourceUrl = page;
107-
const content = await getContentString(page);
108-
rawMarkdownContent = content;
109-
const markdown = await parse(content);
110-
renderedMarkdown = resolveHtmlAssetLinks(markdown.html, sourceUrl);
111-
frontmatter = resolveFrontmatterLinks(
112-
markdown.frontmatter as Record<string, {}> | undefined,
113-
sourceUrl,
114-
);
115-
imports = frontmatter?.imports as string[] | undefined;
185+
if (mirrorSpec) {
186+
sourceUrl = mirrorSpec.rawUrl;
187+
const response = await fetch(mirrorSpec.rawUrl);
188+
if (!response.ok) {
189+
throw redirect({ to: itemPath });
190+
}
191+
const exampleContent = await response.text();
192+
const mirrorMarkdown = buildMirrorMarkdown(mirrorSpec, exampleContent);
193+
rawMarkdownContent = mirrorMarkdown;
194+
const markdown = await parse(mirrorMarkdown);
195+
renderedMarkdown = resolveHtmlAssetLinks(markdown.html, sourceUrl);
196+
frontmatter = resolveFrontmatterLinks(
197+
markdown.frontmatter as Record<string, {}> | undefined,
198+
sourceUrl,
199+
);
200+
imports = frontmatter?.imports as string[] | undefined;
201+
} else {
202+
sourceUrl = page;
203+
const content = await getContentString(page);
204+
rawMarkdownContent = content;
205+
const markdown = await parse(content);
206+
renderedMarkdown = resolveHtmlAssetLinks(markdown.html, sourceUrl);
207+
frontmatter = resolveFrontmatterLinks(
208+
markdown.frontmatter as Record<string, {}> | undefined,
209+
sourceUrl,
210+
);
211+
imports = frontmatter?.imports as string[] | undefined;
212+
}
116213
} else if (item.readme) {
117214
const readme =
118215
typeof item.readme === "object" ? item.readme.en : item.readme;
@@ -353,15 +450,21 @@ function getMarketplacePageNeighbors(
353450

354451
for (const [key, value] of entries) {
355452
if (typeof value === "string") {
356-
const isExternal = !value.endsWith(".md") && !value.endsWith(".html");
453+
const isExternal =
454+
!value.endsWith(".md") &&
455+
!value.endsWith(".html") &&
456+
!isMarketplaceMirrorPage(manifest.id, key);
357457
if (!isExternal) {
358458
allPages.push({ route: key, isExternal });
359459
}
360460
} else {
361461
for (const [route, path] of Object.entries(
362462
value as Record<string, string>,
363463
)) {
364-
const isExternal = !path.endsWith(".md") && !path.endsWith(".html");
464+
const isExternal =
465+
!path.endsWith(".md") &&
466+
!path.endsWith(".html") &&
467+
!isMarketplaceMirrorPage(manifest.id, route);
365468
if (!isExternal) {
366469
allPages.push({ route, isExternal });
367470
}
@@ -385,3 +488,52 @@ function getMarketplacePageNeighbors(
385488
nextRoute: nextRoute || undefined,
386489
};
387490
}
491+
492+
function buildMirrorMarkdown(
493+
spec: MarketplaceMirrorSpec,
494+
exampleMarkdown: string,
495+
) {
496+
const cleaned = stripLeadingMarkdownH1(
497+
stripFrontmatterBlock(exampleMarkdown),
498+
);
499+
return [
500+
"---",
501+
`og:title: ${spec.ogTitle}`,
502+
`og:description: ${spec.ogDescription}`,
503+
`og:image: ${spec.ogImage}`,
504+
`twitter:image: ${spec.ogImage}`,
505+
`description: ${spec.ogDescription}`,
506+
"---",
507+
"",
508+
`![${spec.heroImageAlt}](${spec.heroImage})`,
509+
"",
510+
spec.pitch,
511+
"",
512+
"---",
513+
"",
514+
"> [!NOTE]",
515+
`> This example is mirrored from the official TanStack example in the [${spec.sourceLabel}](${spec.sourceUrl}) repository.`,
516+
"",
517+
cleaned,
518+
"",
519+
].join("\n");
520+
}
521+
522+
function stripFrontmatterBlock(markdown: string) {
523+
if (!markdown.startsWith("---")) return markdown;
524+
const end = markdown.indexOf("\n---", 3);
525+
if (end === -1) return markdown;
526+
return markdown.slice(end + 4).trimStart();
527+
}
528+
529+
function stripLeadingMarkdownH1(markdown: string) {
530+
const lines = markdown.split(/\r?\n/);
531+
let index = 0;
532+
while (index < lines.length && lines[index].trim() === "") {
533+
index += 1;
534+
}
535+
if (index < lines.length && lines[index].startsWith("# ")) {
536+
lines.splice(index, 1);
537+
}
538+
return lines.join("\n").trimStart();
539+
}

packages/website-v2/src/routes/m/$uid/$slug/index.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,15 @@ function buildMarketplaceHead(
5959
description;
6060
const ogMeta = extractOgMeta(data.frontmatter);
6161
const twitterMeta = extractTwitterMeta(data.frontmatter);
62+
const frontmatterImage =
63+
typeof data.frontmatter?.["og:image"] === "string"
64+
? String(data.frontmatter["og:image"])
65+
: undefined;
6266
const image =
63-
data.manifest.gallery && data.manifest.gallery.length > 0
67+
frontmatterImage ||
68+
(data.manifest.gallery && data.manifest.gallery.length > 0
6469
? data.manifest.gallery[0]
65-
: "https://cdn.jsdelivr.net/gh/opral/inlang@latest/packages/website/public/opengraph/inlang-social-image.jpg";
70+
: "https://cdn.jsdelivr.net/gh/opral/inlang@latest/packages/website/public/opengraph/inlang-social-image.jpg");
6671
const canonicalSlug = data.manifest.slug
6772
? data.manifest.slug.replaceAll(".", "-")
6873
: data.manifest.id.replaceAll(".", "-");

0 commit comments

Comments
 (0)