@lexibase/rdx-next 5.1.2 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/bin/cli.mjs +22 -3
- package/bin/homepage-translations.mjs +137 -0
- package/bin/locale-routes.mjs +23 -1
- package/dist/index.d.ts +1 -1
- package/dist/runtime/index.d.ts +12 -4
- package/dist/runtime/index.js +134 -16
- package/package.json +5 -5
- package/templates/runtime/app/page.tsx +10 -2
- package/templates/runtime/rdx-home.ts +2 -0
package/README.md
CHANGED
|
@@ -50,8 +50,8 @@ through `@lexibase/rdx-ui`: logo, navbar, Base UI version dropdown, mobile/deskt
|
|
|
50
50
|
sidebar, landing page, MDX styles and theme provider. The adapter owns content
|
|
51
51
|
and route data; it does not implement a parallel UI theme.
|
|
52
52
|
|
|
53
|
-
Use `homepage: { enabled: true }` in `rdx.config.ts` for the
|
|
54
|
-
|
|
53
|
+
Use `homepage: { enabled: true }` in `rdx.config.ts` for the built-in landing
|
|
54
|
+
page, or import a project-owned React component with `homepage.component`.
|
|
55
55
|
Without the option, `/` redirects to `/docs`. Additional content sources from
|
|
56
56
|
explicitly configured plugins enable the shared navbar dropdown.
|
|
57
57
|
|
package/bin/cli.mjs
CHANGED
|
@@ -26,7 +26,7 @@ export async function runRdxCli({ templateDir } = {}) {
|
|
|
26
26
|
templateDir ?? path.join(packageRoot, 'templates', 'runtime')
|
|
27
27
|
const runtimeDirectory = path.join(projectRoot, '.rdx')
|
|
28
28
|
|
|
29
|
-
async function prepareRuntime(project) {
|
|
29
|
+
async function prepareRuntime(project, homepageTranslations) {
|
|
30
30
|
await mkdir(runtimeDirectory, { recursive: true })
|
|
31
31
|
await preparePublicAssets(projectRoot, runtimeDirectory)
|
|
32
32
|
await cp(runtimeTemplate, runtimeDirectory, {
|
|
@@ -35,6 +35,11 @@ export async function runRdxCli({ templateDir } = {}) {
|
|
|
35
35
|
})
|
|
36
36
|
await prepareProjectStyles(projectRoot, runtimeDirectory)
|
|
37
37
|
await prepareLocaleRoutes(project, runtimeDirectory, runtimeTemplate)
|
|
38
|
+
if (homepageTranslations) {
|
|
39
|
+
const { prepareHomepageTranslations } =
|
|
40
|
+
await import('./homepage-translations.mjs')
|
|
41
|
+
await prepareHomepageTranslations(runtimeDirectory, homepageTranslations)
|
|
42
|
+
}
|
|
38
43
|
// Resolve in Node before Next processes this file: UI may be transitive,
|
|
39
44
|
// and native Tailwind plugins must stay outside the Turbopack module graph.
|
|
40
45
|
const resolvePlugin = createRequire(import.meta.url)
|
|
@@ -140,6 +145,20 @@ export async function runRdxCli({ templateDir } = {}) {
|
|
|
140
145
|
rootDir: projectRoot,
|
|
141
146
|
command,
|
|
142
147
|
})
|
|
148
|
+
let homepageTranslations
|
|
149
|
+
if (
|
|
150
|
+
project.config.homepage?.enabled &&
|
|
151
|
+
project.config.homepage.translations &&
|
|
152
|
+
(supportedCommands.has(command) ||
|
|
153
|
+
command === 'check' ||
|
|
154
|
+
command === 'prepare')
|
|
155
|
+
) {
|
|
156
|
+
const { readHomepageTranslations } =
|
|
157
|
+
await import('./homepage-translations.mjs')
|
|
158
|
+
homepageTranslations = await readHomepageTranslations(project, {
|
|
159
|
+
report: command === 'check' || command === 'build',
|
|
160
|
+
})
|
|
161
|
+
}
|
|
143
162
|
if (command === 'check' || command === 'build') {
|
|
144
163
|
const { checkDocumentation, formatDocumentationCheck } =
|
|
145
164
|
await import('@lexibase/rdx-core/check')
|
|
@@ -153,7 +172,7 @@ export async function runRdxCli({ templateDir } = {}) {
|
|
|
153
172
|
if (command === 'check') return
|
|
154
173
|
}
|
|
155
174
|
if (command === 'prepare') {
|
|
156
|
-
await prepareRuntime(project)
|
|
175
|
+
await prepareRuntime(project, homepageTranslations)
|
|
157
176
|
console.log('RDX runtime prepared in .rdx/')
|
|
158
177
|
return
|
|
159
178
|
}
|
|
@@ -174,7 +193,7 @@ export async function runRdxCli({ templateDir } = {}) {
|
|
|
174
193
|
return
|
|
175
194
|
}
|
|
176
195
|
|
|
177
|
-
await prepareRuntime(project)
|
|
196
|
+
await prepareRuntime(project, homepageTranslations)
|
|
178
197
|
const projectRequire = createRequire(path.join(projectRoot, 'package.json'))
|
|
179
198
|
const nextCli = projectRequire.resolve('next/dist/bin/next')
|
|
180
199
|
const child = spawn(
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
const sourceFile = 'components/home.tsx'
|
|
5
|
+
const metadataKey = '$metadata'
|
|
6
|
+
|
|
7
|
+
function missingFile(error) {
|
|
8
|
+
return error.code === 'ENOENT' || error.code === 'ENOTDIR'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function readOptional(file) {
|
|
12
|
+
try {
|
|
13
|
+
return await readFile(file, 'utf8')
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if (!missingFile(error)) throw error
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function sourcePhrases(rootDir) {
|
|
20
|
+
const source = await readOptional(path.join(rootDir, sourceFile))
|
|
21
|
+
if (source === undefined)
|
|
22
|
+
throw new Error(`Create ${sourceFile} to use homepage.translations.`)
|
|
23
|
+
const { default: ts } = await import('typescript')
|
|
24
|
+
const file = ts.createSourceFile(sourceFile, source, ts.ScriptTarget.Latest)
|
|
25
|
+
for (const statement of file.statements) {
|
|
26
|
+
if (
|
|
27
|
+
!ts.isExpressionStatement(statement) ||
|
|
28
|
+
!ts.isStringLiteral(statement.expression)
|
|
29
|
+
)
|
|
30
|
+
break
|
|
31
|
+
if (statement.expression.text === 'use client')
|
|
32
|
+
throw new Error(
|
|
33
|
+
`${sourceFile} must be a Server Component when homepage.translations is enabled. Move 'use client' into interactive child components.`
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
const phrases = new Set()
|
|
37
|
+
function visit(node) {
|
|
38
|
+
if (
|
|
39
|
+
ts.isCallExpression(node) &&
|
|
40
|
+
ts.isIdentifier(node.expression) &&
|
|
41
|
+
node.expression.text === 't' &&
|
|
42
|
+
node.arguments.length === 1 &&
|
|
43
|
+
ts.isStringLiteralLike(node.arguments[0])
|
|
44
|
+
)
|
|
45
|
+
phrases.add(node.arguments[0].text)
|
|
46
|
+
ts.forEachChild(node, visit)
|
|
47
|
+
}
|
|
48
|
+
visit(file)
|
|
49
|
+
return phrases
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function readHomepageTranslations(
|
|
53
|
+
project,
|
|
54
|
+
{ report = false } = {}
|
|
55
|
+
) {
|
|
56
|
+
const { rootDir, localization } = project
|
|
57
|
+
const phrases = await sourcePhrases(rootDir)
|
|
58
|
+
phrases.delete(metadataKey)
|
|
59
|
+
const catalogs = []
|
|
60
|
+
for (const locale of localization?.locales ?? []) {
|
|
61
|
+
if (!locale.folder) continue
|
|
62
|
+
const file = `locale/${locale.folder}/home.json`
|
|
63
|
+
const raw = await readOptional(path.join(rootDir, file))
|
|
64
|
+
let messages = {}
|
|
65
|
+
if (raw !== undefined) {
|
|
66
|
+
try {
|
|
67
|
+
const parsed = JSON.parse(raw)
|
|
68
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
69
|
+
throw new Error('Expected a JSON object.')
|
|
70
|
+
messages = parsed
|
|
71
|
+
catalogs.push({ file, locale: locale.path })
|
|
72
|
+
} catch {
|
|
73
|
+
console.warn(
|
|
74
|
+
`[RDX] ${file}: expected a JSON object of homepage phrases. Using the primary text.`
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!report) continue
|
|
79
|
+
for (const phrase of phrases) {
|
|
80
|
+
if (
|
|
81
|
+
!Object.hasOwn(messages, phrase) ||
|
|
82
|
+
typeof messages[phrase] !== 'string' ||
|
|
83
|
+
!messages[phrase].trim()
|
|
84
|
+
)
|
|
85
|
+
console.warn(
|
|
86
|
+
`[RDX] ${file}: missing translation for ${JSON.stringify(phrase)}`
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
for (const phrase of Object.keys(messages)) {
|
|
90
|
+
if (phrase !== metadataKey && !phrases.has(phrase))
|
|
91
|
+
console.warn(
|
|
92
|
+
`[RDX] ${file}: orphaned translation for ${JSON.stringify(phrase)}`
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { catalogs, phrases }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function prepareHomepageTranslations(
|
|
100
|
+
runtime,
|
|
101
|
+
{ catalogs, phrases }
|
|
102
|
+
) {
|
|
103
|
+
const imports = catalogs.map(
|
|
104
|
+
({ file }, index) =>
|
|
105
|
+
`import messages${index} from ${JSON.stringify(`../${file}`)}`
|
|
106
|
+
)
|
|
107
|
+
const entries = catalogs.map(
|
|
108
|
+
({ locale }, index) => ` [${JSON.stringify(locale)}, messages${index}],`
|
|
109
|
+
)
|
|
110
|
+
await writeFile(
|
|
111
|
+
path.join(runtime, 'rdx-home.ts'),
|
|
112
|
+
`import translatedComponent from '../components/home'
|
|
113
|
+
${imports.join('\n')}
|
|
114
|
+
|
|
115
|
+
const catalogs = new Map<string, Record<string, unknown>>([
|
|
116
|
+
${entries.join('\n')}
|
|
117
|
+
])
|
|
118
|
+
|
|
119
|
+
const phrases: string[] = ${JSON.stringify([...phrases])}
|
|
120
|
+
|
|
121
|
+
export function getHomepageTranslation(locale?: string) {
|
|
122
|
+
return { translatedComponent, messages: locale ? catalogs.get(locale) : undefined }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function getHomepageSeo(locale?: string) {
|
|
126
|
+
const translatedLocales = new Set<string>()
|
|
127
|
+
for (const [path, catalog] of catalogs) {
|
|
128
|
+
if (phrases.some((phrase) => {
|
|
129
|
+
const text = Object.hasOwn(catalog, phrase) ? catalog[phrase] : undefined
|
|
130
|
+
return typeof text === 'string' && text.trim() && text.trim() !== phrase.trim()
|
|
131
|
+
})) translatedLocales.add(path)
|
|
132
|
+
}
|
|
133
|
+
return { translatedLocales, metadata: locale ? catalogs.get(locale)?.${metadataKey} : undefined }
|
|
134
|
+
}
|
|
135
|
+
`
|
|
136
|
+
)
|
|
137
|
+
}
|
package/bin/locale-routes.mjs
CHANGED
|
@@ -11,6 +11,7 @@ const generated = [
|
|
|
11
11
|
...defaultFiles.map((file) => `app/(rdx-default)/${file}`),
|
|
12
12
|
'app/(rdx-default)/[...notFound]/page.tsx',
|
|
13
13
|
'app/(rdx-locales)/[locale]/layout.tsx',
|
|
14
|
+
'app/(rdx-locales)/[locale]/page.tsx',
|
|
14
15
|
'app/(rdx-locales)/[locale]/not-found.tsx',
|
|
15
16
|
'app/(rdx-locales)/[locale]/docs/[[...slug]]/page.tsx',
|
|
16
17
|
'app/(rdx-locales)/[locale]/rdx-search/[source]/route.ts',
|
|
@@ -83,7 +84,7 @@ export async function prepareLocaleRoutes(project, runtime, template) {
|
|
|
83
84
|
await emit(
|
|
84
85
|
`app/(rdx-default)/${file}`,
|
|
85
86
|
text
|
|
86
|
-
.replace(/from '(\.\.\/[^']*rdx-project)'/g, "from '../$1'")
|
|
87
|
+
.replace(/from '(\.\.\/[^']*rdx-(?:home|project))'/g, "from '../$1'")
|
|
87
88
|
.replace("'./globals.css'", "'../globals.css'")
|
|
88
89
|
)
|
|
89
90
|
}
|
|
@@ -107,6 +108,27 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s
|
|
|
107
108
|
export default async function Layout({ children, params }: { children: React.ReactNode, params: Promise<{ locale: string }> }) {
|
|
108
109
|
return <RdxLayout config={await configuration(params)} bodyClassName="antialiased">{children}</RdxLayout>
|
|
109
110
|
}
|
|
111
|
+
`
|
|
112
|
+
)
|
|
113
|
+
await emit(
|
|
114
|
+
'app/(rdx-locales)/[locale]/page.tsx',
|
|
115
|
+
`import { HomePage, getHomepageMetadata, getLocalizedProject } from '${provider}'
|
|
116
|
+
import { notFound, permanentRedirect } from 'next/navigation'
|
|
117
|
+
import { getHomepageSeo, getHomepageTranslation } from '../../../rdx-home'
|
|
118
|
+
import { getProject } from '../../../rdx-project'
|
|
119
|
+
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
|
|
120
|
+
const project = await getProject()
|
|
121
|
+
const { locale } = await params
|
|
122
|
+
return getHomepageMetadata({ project, locale, ...getHomepageSeo(locale) })
|
|
123
|
+
}
|
|
124
|
+
export default async function Page({ params }: { params: Promise<{ locale: string }> }) {
|
|
125
|
+
const project = await getProject()
|
|
126
|
+
const { locale } = await params
|
|
127
|
+
const selected = project.localization?.locales.find(entry => entry.path === locale)
|
|
128
|
+
if (!project.config.homepage?.enabled || !selected) notFound()
|
|
129
|
+
if (selected.code === project.localization!.defaultLocale) permanentRedirect('/')
|
|
130
|
+
return <HomePage project={getLocalizedProject(project, locale)} {...getHomepageTranslation(locale)} />
|
|
131
|
+
}
|
|
110
132
|
`
|
|
111
133
|
)
|
|
112
134
|
await emit(
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { defineConfig } from '@lexibase/rdx-core/config';
|
|
2
|
-
export { RDXConfig, RdxHomepage, RdxMetadata, RdxPlugin, RdxPluginContext, RdxRuntimeConfig } from '@lexibase/rdx-types';
|
|
2
|
+
export { RDXConfig, RdxHomepage, RdxHomepageProps, RdxMetadata, RdxPlugin, RdxPluginContext, RdxRuntimeConfig, RdxTranslatedHomepageProps } from '@lexibase/rdx-types';
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
3
|
-
import { RdxProject, RDXConfig, RdxContentSource, RdxDocPage } from '@lexibase/rdx-types';
|
|
2
|
+
import { ComponentType, ReactNode } from 'react';
|
|
3
|
+
import { RdxProject, RDXConfig, RdxContentSource, RdxDocPage, RdxTranslatedHomepageProps } from '@lexibase/rdx-types';
|
|
4
4
|
export { createRdxProject, getLocalizedProject, getProjectDocsPaths } from '@lexibase/rdx-core';
|
|
5
5
|
import { MetadataRoute, Metadata } from 'next';
|
|
6
6
|
|
|
@@ -32,6 +32,12 @@ declare function getMarkdownResponse(project: RdxProject, path: string[]): Respo
|
|
|
32
32
|
declare function markdownNotFound(): Response;
|
|
33
33
|
declare function getDocumentMarkdownResponse(project: RdxProject, source: RdxContentSource | undefined, slug: string, documentPath?: DocumentPath): Response;
|
|
34
34
|
|
|
35
|
+
declare function getHomepageMetadata({ project, locale, translatedLocales, metadata, }: {
|
|
36
|
+
project: RdxProject;
|
|
37
|
+
locale?: string;
|
|
38
|
+
translatedLocales?: ReadonlySet<string>;
|
|
39
|
+
metadata?: unknown;
|
|
40
|
+
}): Metadata;
|
|
35
41
|
/** Read metadata from the same content source and index used by the page. */
|
|
36
42
|
declare function getDocumentMetadata({ project, slug, }: {
|
|
37
43
|
project: RdxProject;
|
|
@@ -47,8 +53,10 @@ declare function RdxLayout({ children, config, bodyClassName, }: {
|
|
|
47
53
|
config: RDXConfig;
|
|
48
54
|
bodyClassName?: string;
|
|
49
55
|
}): react.JSX.Element;
|
|
50
|
-
declare function HomePage({ project }: {
|
|
56
|
+
declare function HomePage({ project, translatedComponent: TranslatedHome, messages, }: {
|
|
51
57
|
project: RdxProject;
|
|
58
|
+
translatedComponent?: ComponentType<RdxTranslatedHomepageProps>;
|
|
59
|
+
messages?: Record<string, unknown>;
|
|
52
60
|
}): react.JSX.Element;
|
|
53
61
|
declare function NotFoundPage({ project }: {
|
|
54
62
|
project: RdxProject;
|
|
@@ -58,4 +66,4 @@ declare function DocsPage({ slug, project, }: {
|
|
|
58
66
|
project: RdxProject;
|
|
59
67
|
}): Promise<react.JSX.Element>;
|
|
60
68
|
|
|
61
|
-
export { DocsPage, HomePage, NotFoundPage, RdxLayout, getDocumentMarkdownResponse, getDocumentMetadata, getLlmIndexResponse, getMarkdownHref, getMarkdownPaths, getMarkdownResponse, getPageMetadata, getRdxMetadata, getRdxRobots, getRdxSitemap, getSearchResponse, getSearchSources, getSourceRobots, markdownNotFound, parseMarkdownPath };
|
|
69
|
+
export { DocsPage, HomePage, NotFoundPage, RdxLayout, getDocumentMarkdownResponse, getDocumentMetadata, getHomepageMetadata, getLlmIndexResponse, getMarkdownHref, getMarkdownPaths, getMarkdownResponse, getPageMetadata, getRdxMetadata, getRdxRobots, getRdxSitemap, getSearchResponse, getSearchSources, getSourceRobots, markdownNotFound, parseMarkdownPath };
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/runtime/index.tsx
|
|
2
2
|
import {
|
|
3
3
|
getDocument as getDocument4,
|
|
4
|
+
getDocsIndex as getDocsIndex4,
|
|
4
5
|
getDocsPaths,
|
|
5
6
|
getSourceOptions as getSourceOptions5,
|
|
6
7
|
resolveDocsRoute as resolveDocsRoute2,
|
|
@@ -17,7 +18,7 @@ import { DocsSidebar } from "@lexibase/rdx-ui/components/docs-sidebar";
|
|
|
17
18
|
import { DocsToc } from "@lexibase/rdx-ui/components/docs-toc";
|
|
18
19
|
import { DocsPageTransition } from "@lexibase/rdx-ui/components/docs-page-transition";
|
|
19
20
|
import { DocsSearch } from "@lexibase/rdx-ui/components/docs-search";
|
|
20
|
-
import { Home } from "@lexibase/rdx-ui/components/home";
|
|
21
|
+
import { Home, HomeSection } from "@lexibase/rdx-ui/components/home";
|
|
21
22
|
import { SkipLink } from "@lexibase/rdx-ui/components/skip-link";
|
|
22
23
|
import { FooterCredit } from "@lexibase/rdx-ui/components/footer-credit";
|
|
23
24
|
import { EditPageLink } from "@lexibase/rdx-ui/components/edit-page-link";
|
|
@@ -317,6 +318,67 @@ import {
|
|
|
317
318
|
getSourceOptions as getSourceOptions4
|
|
318
319
|
} from "@lexibase/rdx-core";
|
|
319
320
|
import { notFound } from "next/navigation";
|
|
321
|
+
function getHomepageMetadata({
|
|
322
|
+
project,
|
|
323
|
+
locale,
|
|
324
|
+
translatedLocales,
|
|
325
|
+
metadata
|
|
326
|
+
}) {
|
|
327
|
+
const { config, localization } = project;
|
|
328
|
+
if (!config.homepage?.enabled || !localization) return {};
|
|
329
|
+
const selected = localization.locales.find((entry) => entry.path === locale);
|
|
330
|
+
if (locale && (!selected || selected.code === localization.defaultLocale))
|
|
331
|
+
return {};
|
|
332
|
+
const base = config.metadata.metadataBase;
|
|
333
|
+
const primaryUrl = base ? new URL("/", base).href : void 0;
|
|
334
|
+
if (locale && !translatedLocales?.has(locale)) {
|
|
335
|
+
if (config.homepage.component) return {};
|
|
336
|
+
return {
|
|
337
|
+
robots: { index: false, follow: true },
|
|
338
|
+
...primaryUrl ? { alternates: { canonical: primaryUrl } } : {}
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
const url = base && new URL(locale ? `/${locale}` : "/", base).href;
|
|
342
|
+
const site = getRdxMetadata(config);
|
|
343
|
+
const fields = metadata && typeof metadata === "object" ? metadata : {};
|
|
344
|
+
const title = "title" in fields && typeof fields.title === "string" ? fields.title.trim() : "";
|
|
345
|
+
const description = "description" in fields && typeof fields.description === "string" ? fields.description.trim() : "";
|
|
346
|
+
const defaults = {
|
|
347
|
+
title: config.metadata.applicationName,
|
|
348
|
+
description: config.description
|
|
349
|
+
};
|
|
350
|
+
const social = {
|
|
351
|
+
...title ? { title: { absolute: title } } : {},
|
|
352
|
+
...description ? { description } : {}
|
|
353
|
+
};
|
|
354
|
+
return {
|
|
355
|
+
...social,
|
|
356
|
+
...site.openGraph || title || description ? {
|
|
357
|
+
openGraph: {
|
|
358
|
+
...defaults,
|
|
359
|
+
...site.openGraph,
|
|
360
|
+
...social,
|
|
361
|
+
...url ? { url } : {}
|
|
362
|
+
}
|
|
363
|
+
} : {},
|
|
364
|
+
...site.twitter || title || description ? { twitter: { ...defaults, ...site.twitter, ...social } } : {},
|
|
365
|
+
...base && url && primaryUrl ? {
|
|
366
|
+
alternates: {
|
|
367
|
+
canonical: url,
|
|
368
|
+
languages: {
|
|
369
|
+
[localization.defaultLocale]: primaryUrl,
|
|
370
|
+
"x-default": primaryUrl,
|
|
371
|
+
...Object.fromEntries(
|
|
372
|
+
localization.locales.filter((entry) => translatedLocales?.has(entry.path)).map((entry) => [
|
|
373
|
+
entry.code,
|
|
374
|
+
new URL(`/${entry.path}`, base).href
|
|
375
|
+
])
|
|
376
|
+
)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
} : {}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
320
382
|
function getDocumentMetadata({
|
|
321
383
|
project,
|
|
322
384
|
slug
|
|
@@ -429,9 +491,13 @@ function RdxLayout({
|
|
|
429
491
|
}
|
|
430
492
|
) }) });
|
|
431
493
|
}
|
|
494
|
+
function homepageHref(project, locale) {
|
|
495
|
+
return locale && locale.code !== project.localization?.defaultLocale ? `/${locale.path}` : "/";
|
|
496
|
+
}
|
|
432
497
|
function RdxHeader({
|
|
433
498
|
project,
|
|
434
|
-
slug
|
|
499
|
+
slug,
|
|
500
|
+
onHomepage = false
|
|
435
501
|
}) {
|
|
436
502
|
const { config } = project;
|
|
437
503
|
const messages = getMessages(config.lang);
|
|
@@ -460,14 +526,18 @@ function RdxHeader({
|
|
|
460
526
|
) : void 0,
|
|
461
527
|
siteTitle: config.metadata.applicationName,
|
|
462
528
|
logo: /* @__PURE__ */ jsx(BrandLogo, { source: config.navbar?.logo, className: "size-5" }),
|
|
463
|
-
homeHref: config.homepage?.enabled ?
|
|
529
|
+
homeHref: config.homepage?.enabled ? homepageHref(project, project.locale) : getDocsHref4(project),
|
|
464
530
|
docsHref: getDocsHref4(project, source),
|
|
465
531
|
languageMenu: project.localization ? /* @__PURE__ */ jsx(
|
|
466
532
|
LanguageDropdown,
|
|
467
533
|
{
|
|
468
534
|
lang: config.lang,
|
|
469
535
|
current: project.locale?.code ?? project.localization.defaultLocale,
|
|
470
|
-
options:
|
|
536
|
+
options: onHomepage ? project.localization.locales.map((locale) => ({
|
|
537
|
+
code: locale.code,
|
|
538
|
+
label: locale.label,
|
|
539
|
+
href: homepageHref(project, locale)
|
|
540
|
+
})) : getLanguageLinks(project, source, currentSlug),
|
|
471
541
|
contributionUrl: project.localization.contributionUrl
|
|
472
542
|
}
|
|
473
543
|
) : void 0,
|
|
@@ -477,32 +547,79 @@ function RdxHeader({
|
|
|
477
547
|
}
|
|
478
548
|
);
|
|
479
549
|
}
|
|
480
|
-
function HomePage({
|
|
550
|
+
function HomePage({
|
|
551
|
+
project,
|
|
552
|
+
translatedComponent: TranslatedHome,
|
|
553
|
+
messages
|
|
554
|
+
}) {
|
|
481
555
|
const { config } = project;
|
|
482
|
-
|
|
556
|
+
const docsHref = getDocsHref4(project);
|
|
557
|
+
if (!config.homepage?.enabled) redirect(docsHref);
|
|
483
558
|
const homepage = config.homepage;
|
|
484
559
|
const CustomHome = homepage.component;
|
|
560
|
+
const docsLabel = getMessages(config.lang).docs;
|
|
561
|
+
const source = project.sources[0];
|
|
562
|
+
const categories = CustomHome || TranslatedHome ? [] : getDocsIndex4(getSourceOptions5(project, source));
|
|
563
|
+
const firstPage = categories[0]?.pages[0];
|
|
485
564
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
486
|
-
/* @__PURE__ */ jsx(RdxHeader, { project }),
|
|
565
|
+
/* @__PURE__ */ jsx(RdxHeader, { project, onHomepage: true }),
|
|
487
566
|
/* @__PURE__ */ jsx(
|
|
488
567
|
"main",
|
|
489
568
|
{
|
|
490
569
|
id: "rdx-main",
|
|
491
570
|
tabIndex: -1,
|
|
492
|
-
className: "flex-1 min-h-0 overflow-y-auto",
|
|
493
|
-
children: CustomHome ? /* @__PURE__ */ jsx(CustomHome, {}) : /* @__PURE__ */ jsx(
|
|
571
|
+
className: "relative flex-1 min-h-0 overflow-y-auto",
|
|
572
|
+
children: CustomHome ? /* @__PURE__ */ jsx(CustomHome, { lang: config.lang, docsHref }) : TranslatedHome ? /* @__PURE__ */ jsx(
|
|
573
|
+
TranslatedHome,
|
|
574
|
+
{
|
|
575
|
+
lang: config.lang,
|
|
576
|
+
docsHref,
|
|
577
|
+
t: (text2) => {
|
|
578
|
+
const translation = messages && Object.hasOwn(messages, text2) ? messages[text2] : void 0;
|
|
579
|
+
return typeof translation === "string" && translation.trim() ? translation : text2;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
) : /* @__PURE__ */ jsx(
|
|
494
583
|
Home,
|
|
495
584
|
{
|
|
496
585
|
title: homepage.title ?? config.metadata.applicationName,
|
|
497
|
-
logo: /* @__PURE__ */ jsx(
|
|
498
|
-
|
|
586
|
+
logo: /* @__PURE__ */ jsx(BrandLogo, { source: config.navbar?.logo, className: "size-5" }),
|
|
587
|
+
description: homepage.description ?? config.description,
|
|
588
|
+
buttons: homepage.buttons?.map((button) => ({
|
|
589
|
+
...button,
|
|
590
|
+
href: project.locale && /^\/docs(?:[/?#]|$)/.test(button.href) ? `/${project.locale.path}${button.href}` : button.href
|
|
591
|
+
})) ?? [{ label: docsLabel, href: docsHref }],
|
|
592
|
+
previewTitle: firstPage?.title,
|
|
593
|
+
previewDescription: firstPage?.description,
|
|
594
|
+
children: categories.length > 0 && /* @__PURE__ */ jsx(
|
|
595
|
+
HomeSection,
|
|
499
596
|
{
|
|
500
|
-
|
|
501
|
-
className: "
|
|
597
|
+
"aria-labelledby": "home-docs-title",
|
|
598
|
+
className: "border-t bg-muted/20",
|
|
599
|
+
children: /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-6xl", children: [
|
|
600
|
+
/* @__PURE__ */ jsx(
|
|
601
|
+
"h2",
|
|
602
|
+
{
|
|
603
|
+
id: "home-docs-title",
|
|
604
|
+
className: "text-3xl font-semibold tracking-tight sm:text-4xl",
|
|
605
|
+
children: docsLabel
|
|
606
|
+
}
|
|
607
|
+
),
|
|
608
|
+
/* @__PURE__ */ jsx("ul", { className: "mt-6 grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: categories.map((category) => {
|
|
609
|
+
const page = category.pages[0];
|
|
610
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
611
|
+
Button,
|
|
612
|
+
{
|
|
613
|
+
href: getDocsHref4(project, source, page.slug),
|
|
614
|
+
variant: "link",
|
|
615
|
+
className: "h-auto whitespace-normal p-0 text-left",
|
|
616
|
+
children: category.title
|
|
617
|
+
}
|
|
618
|
+
) }, category.id);
|
|
619
|
+
}) })
|
|
620
|
+
] })
|
|
502
621
|
}
|
|
503
|
-
)
|
|
504
|
-
description: homepage.description,
|
|
505
|
-
buttons: homepage.buttons
|
|
622
|
+
)
|
|
506
623
|
}
|
|
507
624
|
)
|
|
508
625
|
}
|
|
@@ -765,6 +882,7 @@ export {
|
|
|
765
882
|
createRdxProject,
|
|
766
883
|
getDocumentMarkdownResponse,
|
|
767
884
|
getDocumentMetadata,
|
|
885
|
+
getHomepageMetadata,
|
|
768
886
|
getLlmIndexResponse,
|
|
769
887
|
getLocalizedProject4 as getLocalizedProject,
|
|
770
888
|
getMarkdownHref,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lexibase/rdx-next",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "The Next.js runtime for RDX documentation projects.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"next-mdx-remote": "^6.0.0",
|
|
53
53
|
"remark-gfm": "^4",
|
|
54
54
|
"typescript": "6.0.3",
|
|
55
|
-
"@lexibase/rdx-core": "3.
|
|
56
|
-
"@lexibase/rdx-types": "2.
|
|
55
|
+
"@lexibase/rdx-core": "3.2.0",
|
|
56
|
+
"@lexibase/rdx-types": "2.6.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@lexibase/rdx-ui": "^3.5.0",
|
|
@@ -81,9 +81,9 @@
|
|
|
81
81
|
"next": "^16.4.2",
|
|
82
82
|
"react": "19.3.1",
|
|
83
83
|
"react-dom": "19.3.1",
|
|
84
|
-
"@
|
|
84
|
+
"@rdx/eslint-config": "1.0.0",
|
|
85
85
|
"@rdx/typescript-config": "1.0.0",
|
|
86
|
-
"@rdx
|
|
86
|
+
"@lexibase/rdx-ui": "3.5.5"
|
|
87
87
|
},
|
|
88
88
|
"scripts": {
|
|
89
89
|
"dev": "tsup src/index.ts src/runtime/index.tsx --format esm --dts --watch --external @lexibase/rdx-core --external @lexibase/rdx-types --external @lexibase/rdx-ui --external next --external react --external react-dom --external next-mdx-remote",
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import { HomePage } from '@lexibase/rdx-next/runtime'
|
|
1
|
+
import { getHomepageMetadata, HomePage } from '@lexibase/rdx-next/runtime'
|
|
2
2
|
|
|
3
|
+
import { getHomepageSeo, getHomepageTranslation } from '../rdx-home'
|
|
3
4
|
import { getProject } from '../rdx-project'
|
|
4
5
|
|
|
6
|
+
export async function generateMetadata() {
|
|
7
|
+
return getHomepageMetadata({
|
|
8
|
+
project: await getProject(),
|
|
9
|
+
...getHomepageSeo(),
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
export default async function Home() {
|
|
6
|
-
return <HomePage project={await getProject()} />
|
|
14
|
+
return <HomePage project={await getProject()} {...getHomepageTranslation()} />
|
|
7
15
|
}
|