@lexibase/rdx-next 3.2.0 → 4.1.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 +14 -2
- package/bin/add-plugin.mjs +111 -35
- package/bin/cli.mjs +63 -7
- package/bin/i18n-setup.mjs +115 -0
- package/bin/locale-routes.mjs +138 -0
- package/bin/plugin-config.mjs +47 -13
- package/config.mjs +27 -6
- package/dist/runtime/index.d.ts +16 -4
- package/dist/runtime/index.js +246 -34
- package/package.json +6 -5
- package/templates/runtime/app/globals.css +1 -0
- package/templates/runtime/app/robots.ts +6 -0
- package/templates/runtime/app/sitemap.ts +6 -0
package/bin/plugin-config.mjs
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
import ts from 'typescript'
|
|
2
2
|
|
|
3
3
|
export const versioningPackage = '@lexibase/rdx-versioning'
|
|
4
|
-
const
|
|
4
|
+
export const i18nPackage = '@lexibase/rdx-i18n'
|
|
5
|
+
const versioning = {
|
|
6
|
+
packageName: versioningPackage,
|
|
7
|
+
name: 'versioning',
|
|
8
|
+
label: 'Versioning',
|
|
9
|
+
}
|
|
10
|
+
const localization = { packageName: i18nPackage, name: 'i18n', label: 'i18n' }
|
|
11
|
+
|
|
12
|
+
export function activateVersioning(source) {
|
|
13
|
+
return editPluginConfig(source, versioning)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function inspectI18n(source) {
|
|
17
|
+
return editPluginConfig(source, localization, '', true)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function activateI18n(source, locales) {
|
|
21
|
+
return editPluginConfig(
|
|
22
|
+
source,
|
|
23
|
+
localization,
|
|
24
|
+
JSON.stringify({ locales }, null, 2)
|
|
25
|
+
)
|
|
26
|
+
}
|
|
5
27
|
|
|
6
28
|
function unwrap(node) {
|
|
7
29
|
while (
|
|
@@ -14,7 +36,9 @@ function unwrap(node) {
|
|
|
14
36
|
}
|
|
15
37
|
|
|
16
38
|
// Edit only known literal shapes; never print/reconstruct the user's entire file.
|
|
17
|
-
|
|
39
|
+
function editPluginConfig(source, plugin, argument = '', inspect = false) {
|
|
40
|
+
const { packageName, name: pluginName, label } = plugin
|
|
41
|
+
const manual = `Cannot safely edit rdx.config.ts. Add "import { ${pluginName} } from '${packageName}'" and ${pluginName}(${pluginName === 'i18n' ? '{ locales: [...] }' : ''}) to plugins manually, then run rdx prepare.`
|
|
18
42
|
const file = ts.createSourceFile(
|
|
19
43
|
'rdx.config.ts',
|
|
20
44
|
source,
|
|
@@ -44,7 +68,7 @@ export function activateVersioning(source) {
|
|
|
44
68
|
imported === 'defineConfig'
|
|
45
69
|
)
|
|
46
70
|
definitions.add(specifier.name.text)
|
|
47
|
-
if (name ===
|
|
71
|
+
if (name === packageName && imported === pluginName) {
|
|
48
72
|
if (factory) throw new Error(manual)
|
|
49
73
|
factory = specifier.name.text
|
|
50
74
|
}
|
|
@@ -52,10 +76,10 @@ export function activateVersioning(source) {
|
|
|
52
76
|
} else if (
|
|
53
77
|
bindings &&
|
|
54
78
|
ts.isNamespaceImport(bindings) &&
|
|
55
|
-
name ===
|
|
79
|
+
name === packageName
|
|
56
80
|
) {
|
|
57
81
|
if (factory) throw new Error(manual)
|
|
58
|
-
factory = `${bindings.name.text}
|
|
82
|
+
factory = `${bindings.name.text}.${pluginName}`
|
|
59
83
|
}
|
|
60
84
|
}
|
|
61
85
|
const exports = file.statements.filter(ts.isExportAssignment)
|
|
@@ -100,9 +124,18 @@ export function activateVersioning(source) {
|
|
|
100
124
|
)
|
|
101
125
|
if (matches.length > 1)
|
|
102
126
|
throw new Error(
|
|
103
|
-
|
|
127
|
+
`${label} is configured more than once. Remove the duplicate plugin before retrying.`
|
|
104
128
|
)
|
|
105
|
-
if (matches.length === 1) return source
|
|
129
|
+
if (matches.length === 1) return inspect ? { configured: true } : source
|
|
130
|
+
}
|
|
131
|
+
if (inspect) {
|
|
132
|
+
const languages = properties.filter((entry) => entry.name.text === 'lang')
|
|
133
|
+
if (languages.length > 1) throw new Error(manual)
|
|
134
|
+
const language = languages.length
|
|
135
|
+
? unwrap(languages[0].initializer)
|
|
136
|
+
: undefined
|
|
137
|
+
if (language && !ts.isStringLiteral(language)) throw new Error(manual)
|
|
138
|
+
return { configured: false, lang: language?.text ?? 'en' }
|
|
106
139
|
}
|
|
107
140
|
const newline = source.includes('\r\n') ? '\r\n' : '\n'
|
|
108
141
|
const edits = []
|
|
@@ -113,11 +146,12 @@ export function activateVersioning(source) {
|
|
|
113
146
|
ts.forEachChild(node, visit)
|
|
114
147
|
}
|
|
115
148
|
visit(file)
|
|
116
|
-
factory =
|
|
149
|
+
factory = pluginName
|
|
117
150
|
let suffix = 1
|
|
118
|
-
|
|
151
|
+
const alias = `rdx${pluginName[0].toUpperCase()}${pluginName.slice(1)}`
|
|
152
|
+
while (identifiers.has(factory)) factory = `${alias}${suffix++}`
|
|
119
153
|
const specifier =
|
|
120
|
-
factory ===
|
|
154
|
+
factory === pluginName ? pluginName : `${pluginName} as ${factory}`
|
|
121
155
|
let position = imports.length
|
|
122
156
|
? imports.at(-1).end
|
|
123
157
|
: file.statements[0].getStart(file)
|
|
@@ -133,7 +167,7 @@ export function activateVersioning(source) {
|
|
|
133
167
|
}
|
|
134
168
|
edits.push({
|
|
135
169
|
position,
|
|
136
|
-
text: `${newline}import { ${specifier} } from '${
|
|
170
|
+
text: `${newline}import { ${specifier} } from '${packageName}'${newline}`,
|
|
137
171
|
})
|
|
138
172
|
}
|
|
139
173
|
if (plugins) {
|
|
@@ -142,14 +176,14 @@ export function activateVersioning(source) {
|
|
|
142
176
|
: plugins.getStart(file) + 1
|
|
143
177
|
edits.push({
|
|
144
178
|
position,
|
|
145
|
-
text: `${plugins.elements.length ? ',' : ''} ${factory}()`,
|
|
179
|
+
text: `${plugins.elements.length ? ',' : ''} ${factory}(${argument.replaceAll('\n', newline)})`,
|
|
146
180
|
})
|
|
147
181
|
} else {
|
|
148
182
|
if (properties.length && !properties.hasTrailingComma)
|
|
149
183
|
edits.push({ position: properties.at(-1).end, text: ',' })
|
|
150
184
|
edits.push({
|
|
151
185
|
position: config.end - 1,
|
|
152
|
-
text: `${newline} plugins: [${factory}()],${newline}`,
|
|
186
|
+
text: `${newline} plugins: [${factory}(${argument.replaceAll('\n', newline)})],${newline}`,
|
|
153
187
|
})
|
|
154
188
|
}
|
|
155
189
|
for (const edit of edits.sort((a, b) => b.position - a.position))
|
package/config.mjs
CHANGED
|
@@ -2,7 +2,11 @@ import { readdir, realpath, stat } from 'node:fs/promises'
|
|
|
2
2
|
import { createRequire } from 'node:module'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
createRdxProject,
|
|
7
|
+
resolveProjectPath,
|
|
8
|
+
getContentSources,
|
|
9
|
+
} from '@lexibase/rdx-core/config'
|
|
6
10
|
import { createJiti } from 'jiti'
|
|
7
11
|
|
|
8
12
|
/** Include the project and the real Next.js installation in Turbopack's root. */
|
|
@@ -77,7 +81,7 @@ export async function getRdxTracingConfig({ rootDir, appDir = rootDir }) {
|
|
|
77
81
|
const project = await createRdxProject(input, { rootDir })
|
|
78
82
|
const files = (
|
|
79
83
|
await Promise.all(
|
|
80
|
-
project
|
|
84
|
+
getContentSources(project)
|
|
81
85
|
.filter((source) => source.redirectTo === undefined)
|
|
82
86
|
.map((source) =>
|
|
83
87
|
contentFiles(
|
|
@@ -102,11 +106,28 @@ export async function getRdxTracingConfig({ rootDir, appDir = rootDir }) {
|
|
|
102
106
|
}
|
|
103
107
|
const redirects = project.sources
|
|
104
108
|
.filter((source) => source.redirectTo !== undefined)
|
|
105
|
-
.
|
|
106
|
-
|
|
107
|
-
|
|
109
|
+
.flatMap((source) =>
|
|
110
|
+
[
|
|
111
|
+
'/docs',
|
|
112
|
+
...(project.localization?.locales.map(
|
|
113
|
+
(locale) => `/${locale.path}/docs`
|
|
114
|
+
) ?? []),
|
|
115
|
+
].map((base) => ({
|
|
116
|
+
source: `${base}/${source.routePrefix}/:path*`,
|
|
117
|
+
destination: `${base}/${source.redirectTo}/:path*`,
|
|
118
|
+
permanent: true,
|
|
119
|
+
}))
|
|
120
|
+
)
|
|
121
|
+
if (project.localization) {
|
|
122
|
+
const primary = project.localization.locales.find(
|
|
123
|
+
(locale) => locale.code === project.localization.defaultLocale
|
|
124
|
+
)
|
|
125
|
+
redirects.push({
|
|
126
|
+
source: '/docs/:path*',
|
|
127
|
+
destination: `/${primary.path}/docs/:path*`,
|
|
108
128
|
permanent: true,
|
|
109
|
-
})
|
|
129
|
+
})
|
|
130
|
+
}
|
|
110
131
|
return {
|
|
111
132
|
...(redirects.length ? { redirects: async () => redirects } : {}),
|
|
112
133
|
outputFileTracingIncludes: {
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { RdxProject, RDXConfig, RdxDocPage } from '@lexibase/rdx-types';
|
|
4
|
-
export { createRdxProject, getProjectDocsPaths } from '@lexibase/rdx-core';
|
|
5
|
-
import { Metadata } from 'next';
|
|
3
|
+
import { RdxProject, RDXConfig, RdxContentSource, RdxDocPage } from '@lexibase/rdx-types';
|
|
4
|
+
export { createRdxProject, getLocalizedProject, getProjectDocsPaths } from '@lexibase/rdx-core';
|
|
5
|
+
import { MetadataRoute, Metadata } from 'next';
|
|
6
6
|
|
|
7
7
|
declare function getSearchSources(project: RdxProject): {
|
|
8
8
|
source: string;
|
|
9
9
|
}[];
|
|
10
10
|
declare function getSearchResponse(project: RdxProject, key: string): Response;
|
|
11
11
|
|
|
12
|
+
/** Published editions replace live development content in search results. */
|
|
13
|
+
declare function getSourceRobots(project: RdxProject, source: RdxContentSource): {
|
|
14
|
+
index: boolean;
|
|
15
|
+
follow: boolean;
|
|
16
|
+
};
|
|
17
|
+
/** Use the configured public URL only; development hosts are never inferred. */
|
|
18
|
+
declare function getRdxRobots(config: RDXConfig): MetadataRoute.Robots;
|
|
19
|
+
/** Share the indexing policy while allowing host applications to map their URLs. */
|
|
20
|
+
declare function getRdxSitemap(project: RdxProject, { documentPath, }?: {
|
|
21
|
+
documentPath?: (source: RdxContentSource, page: RdxDocPage) => string;
|
|
22
|
+
}): MetadataRoute.Sitemap;
|
|
23
|
+
|
|
12
24
|
/** Read metadata from the same content source and index used by the page. */
|
|
13
25
|
declare function getDocumentMetadata({ project, slug, }: {
|
|
14
26
|
project: RdxProject;
|
|
@@ -35,4 +47,4 @@ declare function DocsPage({ slug, project, }: {
|
|
|
35
47
|
project: RdxProject;
|
|
36
48
|
}): Promise<react.JSX.Element>;
|
|
37
49
|
|
|
38
|
-
export { DocsPage, HomePage, NotFoundPage, RdxLayout, getDocumentMetadata, getPageMetadata, getRdxMetadata, getSearchResponse, getSearchSources };
|
|
50
|
+
export { DocsPage, HomePage, NotFoundPage, RdxLayout, getDocumentMetadata, getPageMetadata, getRdxMetadata, getRdxRobots, getRdxSitemap, getSearchResponse, getSearchSources, getSourceRobots };
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
// src/runtime/index.tsx
|
|
2
2
|
import {
|
|
3
|
-
getDocument as
|
|
3
|
+
getDocument as getDocument3,
|
|
4
4
|
getDocsPaths,
|
|
5
|
-
getSourceOptions as
|
|
6
|
-
resolveDocsRoute as resolveDocsRoute2
|
|
5
|
+
getSourceOptions as getSourceOptions4,
|
|
6
|
+
resolveDocsRoute as resolveDocsRoute2,
|
|
7
|
+
getLocalizedProject as getLocalizedProject2,
|
|
8
|
+
getDocsHref as getDocsHref3,
|
|
9
|
+
getLanguageLinks,
|
|
10
|
+
isFallbackPage as isFallbackPage3
|
|
7
11
|
} from "@lexibase/rdx-core";
|
|
8
12
|
import { Button } from "@lexibase/rdx-ui/components/button";
|
|
13
|
+
import { Callout } from "@lexibase/rdx-ui/components/callout";
|
|
14
|
+
import { LanguageDropdown } from "@lexibase/rdx-ui/components/language-dropdown";
|
|
9
15
|
import { DocsSidebar } from "@lexibase/rdx-ui/components/docs-sidebar";
|
|
10
16
|
import { DocsToc } from "@lexibase/rdx-ui/components/docs-toc";
|
|
11
17
|
import { DocsPageTransition } from "@lexibase/rdx-ui/components/docs-page-transition";
|
|
@@ -19,6 +25,7 @@ import { getMessages, formatMessage } from "@lexibase/rdx-ui/lib/i18n";
|
|
|
19
25
|
import { LocaleProvider } from "@lexibase/rdx-ui/providers/locale-provider";
|
|
20
26
|
import { Navbar } from "@lexibase/rdx-ui/components/navbar";
|
|
21
27
|
import { NotFound } from "@lexibase/rdx-ui/components/not-found";
|
|
28
|
+
import { LocalizedNotFound } from "@lexibase/rdx-ui/components/localized-not-found";
|
|
22
29
|
import {
|
|
23
30
|
VersionDropdown
|
|
24
31
|
} from "@lexibase/rdx-ui/components/version-dropdown";
|
|
@@ -54,50 +61,156 @@ function compileDocumentation(source, components, links) {
|
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
// src/runtime/index.tsx
|
|
57
|
-
import {
|
|
64
|
+
import {
|
|
65
|
+
createRdxProject,
|
|
66
|
+
getProjectDocsPaths,
|
|
67
|
+
getLocalizedProject as getLocalizedProject3
|
|
68
|
+
} from "@lexibase/rdx-core";
|
|
58
69
|
|
|
59
70
|
// src/runtime/search.ts
|
|
60
|
-
import {
|
|
71
|
+
import {
|
|
72
|
+
getDocsIndex,
|
|
73
|
+
getDocument,
|
|
74
|
+
getSourceOptions,
|
|
75
|
+
getDocsHref
|
|
76
|
+
} from "@lexibase/rdx-core";
|
|
61
77
|
import { buildSearchIndex } from "@lexibase/rdx-core/search";
|
|
62
78
|
function getSearchSources(project) {
|
|
63
79
|
return project.config.search ? project.sources.filter((source) => source.redirectTo === void 0).map((source) => ({ source: source.routePrefix || "_" })) : [];
|
|
64
80
|
}
|
|
65
81
|
function getSearchResponse(project, key) {
|
|
66
|
-
|
|
82
|
+
const headers = { "X-Robots-Tag": "noindex, follow" };
|
|
83
|
+
if (!project.config.search)
|
|
84
|
+
return new Response(null, { status: 404, headers });
|
|
67
85
|
const source = project.sources.find(
|
|
68
86
|
(entry) => entry.redirectTo === void 0 && (entry.routePrefix || "_") === key
|
|
69
87
|
);
|
|
70
|
-
if (!source) return new Response(null, { status: 404 });
|
|
71
|
-
|
|
88
|
+
if (!source) return new Response(null, { status: 404, headers });
|
|
89
|
+
if (source.fallbackOnly)
|
|
90
|
+
return Response.json(buildSearchIndex([]), { headers });
|
|
91
|
+
const { docsDir } = getSourceOptions(project, source);
|
|
92
|
+
const options = { docsDir };
|
|
72
93
|
const documents = getDocsIndex(options).flatMap(
|
|
73
94
|
(category) => category.pages.map((page) => ({
|
|
74
95
|
title: page.title,
|
|
75
96
|
description: page.description,
|
|
76
97
|
category: category.title,
|
|
77
|
-
href:
|
|
78
|
-
"/docs",
|
|
79
|
-
source.routePrefix,
|
|
80
|
-
page.slug.split("/").map(encodeURIComponent).join("/")
|
|
81
|
-
].filter(Boolean).join("/"),
|
|
98
|
+
href: getDocsHref(project, source, page.slug),
|
|
82
99
|
content: getDocument(page.slug, options)?.content ?? ""
|
|
83
100
|
}))
|
|
84
101
|
);
|
|
85
|
-
return Response.json(buildSearchIndex(documents));
|
|
102
|
+
return Response.json(buildSearchIndex(documents), { headers });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/runtime/indexing.ts
|
|
106
|
+
import {
|
|
107
|
+
getDocsIndex as getDocsIndex2,
|
|
108
|
+
getSourceOptions as getSourceOptions2,
|
|
109
|
+
getContentSources,
|
|
110
|
+
isFallbackPage
|
|
111
|
+
} from "@lexibase/rdx-core";
|
|
112
|
+
function getSourceRobots(project, source) {
|
|
113
|
+
const published = project.sources.some(
|
|
114
|
+
(entry) => entry.id !== "current" && entry.redirectTo === void 0 && entry.badge !== "Archived"
|
|
115
|
+
);
|
|
116
|
+
return {
|
|
117
|
+
index: source.redirectTo === void 0 && source.badge !== "Archived" && (source.id !== "current" || !published),
|
|
118
|
+
follow: true
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function getRdxRobots(config) {
|
|
122
|
+
return {
|
|
123
|
+
// Do not disallow noindex pages: crawlers must read their HTML directive.
|
|
124
|
+
rules: { userAgent: "*", allow: "/" },
|
|
125
|
+
...config.metadata.metadataBase ? { sitemap: new URL("/sitemap.xml", config.metadata.metadataBase).href } : {}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function getRdxSitemap(project, {
|
|
129
|
+
documentPath = (source, page) => [
|
|
130
|
+
source.basePath ?? "/docs",
|
|
131
|
+
source.routePrefix,
|
|
132
|
+
page.slug.split("/").map(encodeURIComponent).join("/")
|
|
133
|
+
].filter(Boolean).join("/")
|
|
134
|
+
} = {}) {
|
|
135
|
+
const base = project.config.metadata.metadataBase;
|
|
136
|
+
if (!base) return [];
|
|
137
|
+
const urls = /* @__PURE__ */ new Set();
|
|
138
|
+
if (project.config.homepage?.enabled) urls.add(new URL("/", base).href);
|
|
139
|
+
for (const source of getContentSources(project)) {
|
|
140
|
+
if (!getSourceRobots(project, source).index) continue;
|
|
141
|
+
for (const category of getDocsIndex2(getSourceOptions2(project, source))) {
|
|
142
|
+
for (const page of category.pages) {
|
|
143
|
+
if (isFallbackPage(project, source, page.sourcePath)) continue;
|
|
144
|
+
urls.add(new URL(documentPath(source, page), base).href);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return [...urls].map((url) => ({ url }));
|
|
86
149
|
}
|
|
87
150
|
|
|
88
151
|
// src/runtime/metadata.ts
|
|
89
|
-
import {
|
|
152
|
+
import {
|
|
153
|
+
resolveDocsRoute,
|
|
154
|
+
getLocalizedProject,
|
|
155
|
+
getDocsHref as getDocsHref2,
|
|
156
|
+
isFallbackPage as isFallbackPage2,
|
|
157
|
+
getDocument as getDocument2,
|
|
158
|
+
getSourceOptions as getSourceOptions3
|
|
159
|
+
} from "@lexibase/rdx-core";
|
|
90
160
|
import { notFound } from "next/navigation";
|
|
91
161
|
function getDocumentMetadata({
|
|
92
162
|
project,
|
|
93
163
|
slug
|
|
94
164
|
}) {
|
|
165
|
+
if (project.localization && !project.locale)
|
|
166
|
+
return { robots: { index: false, follow: true } };
|
|
95
167
|
const route = resolveDocsRoute(project, slug);
|
|
96
|
-
if (route.source.redirectTo !== void 0)
|
|
97
|
-
|
|
168
|
+
if (route.source.redirectTo !== void 0)
|
|
169
|
+
return { robots: getSourceRobots(project, route.source) };
|
|
170
|
+
if (!route.slug) return { robots: { index: false, follow: true } };
|
|
98
171
|
if (!route.document) notFound();
|
|
99
172
|
const page = route.categories.flatMap((category) => category.pages).find((page2) => page2.slug === route.slug);
|
|
100
|
-
|
|
173
|
+
const fallback = isFallbackPage2(project, route.source, page.sourcePath);
|
|
174
|
+
const primary = project.localization?.locales.find(
|
|
175
|
+
(locale) => locale.code === project.localization.defaultLocale
|
|
176
|
+
);
|
|
177
|
+
const canonicalProject = fallback ? getLocalizedProject(project, primary.path) : project;
|
|
178
|
+
const base = project.config.metadata.metadataBase;
|
|
179
|
+
const languages = project.localization && base ? Object.fromEntries(
|
|
180
|
+
project.localization.locales.flatMap((locale) => {
|
|
181
|
+
const view = getLocalizedProject(project, locale.path);
|
|
182
|
+
const source = view.sources.find(
|
|
183
|
+
(entry) => entry.id === route.source.id
|
|
184
|
+
);
|
|
185
|
+
return !source.fallbackOnly && getDocument2(page.slug, {
|
|
186
|
+
docsDir: getSourceOptions3(view, source).docsDir
|
|
187
|
+
}) ? [
|
|
188
|
+
[
|
|
189
|
+
locale.code,
|
|
190
|
+
new URL(getDocsHref2(view, source, page.slug), base).href
|
|
191
|
+
]
|
|
192
|
+
] : [];
|
|
193
|
+
})
|
|
194
|
+
) : void 0;
|
|
195
|
+
return {
|
|
196
|
+
...getPageMetadata(project.config, page),
|
|
197
|
+
robots: fallback ? { index: false, follow: true } : getSourceRobots(project, route.source),
|
|
198
|
+
...project.localization && base ? {
|
|
199
|
+
alternates: {
|
|
200
|
+
canonical: new URL(
|
|
201
|
+
getDocsHref2(
|
|
202
|
+
canonicalProject,
|
|
203
|
+
canonicalProject.sources.find(
|
|
204
|
+
(entry) => entry.id === route.source.id
|
|
205
|
+
),
|
|
206
|
+
page.slug
|
|
207
|
+
),
|
|
208
|
+
base
|
|
209
|
+
).href,
|
|
210
|
+
...fallback ? {} : { languages }
|
|
211
|
+
}
|
|
212
|
+
} : {}
|
|
213
|
+
};
|
|
101
214
|
}
|
|
102
215
|
function getPageMetadata(config, page) {
|
|
103
216
|
const title = page.title;
|
|
@@ -117,6 +230,7 @@ function getRdxMetadata(config) {
|
|
|
117
230
|
title: { default: name, template: `%s | ${name}` },
|
|
118
231
|
description: config.description,
|
|
119
232
|
...metadata,
|
|
233
|
+
robots: { index: true, follow: true },
|
|
120
234
|
...metadataBase ? { metadataBase: new URL(metadataBase) } : {},
|
|
121
235
|
...metadata.openGraph ? {
|
|
122
236
|
openGraph: {
|
|
@@ -164,13 +278,13 @@ function RdxHeader({
|
|
|
164
278
|
const messages = getMessages(config.lang);
|
|
165
279
|
const { source, slug: currentSlug } = resolveDocsRoute2(project, slug);
|
|
166
280
|
const options = project.sources.filter((edition) => edition.redirectTo === void 0).map((edition) => {
|
|
167
|
-
const docs =
|
|
168
|
-
const destination = currentSlug &&
|
|
281
|
+
const docs = getSourceOptions4(project, edition);
|
|
282
|
+
const destination = currentSlug && getDocument3(currentSlug, docs) ? currentSlug : getDocsPaths(docs)[0];
|
|
169
283
|
return {
|
|
170
284
|
id: edition.id,
|
|
171
285
|
label: edition.id === "current" ? messages.current : formatMessage(messages.version, { version: edition.label }),
|
|
172
286
|
kind: edition.id === "current" ? "live" : edition.badge === "Archived" ? "archived" : "version",
|
|
173
|
-
href:
|
|
287
|
+
href: getDocsHref3(project, edition, destination)
|
|
174
288
|
};
|
|
175
289
|
});
|
|
176
290
|
const BrandLogo = config.navbar?.logo ?? Logo;
|
|
@@ -182,14 +296,23 @@ function RdxHeader({
|
|
|
182
296
|
search: config.search ? /* @__PURE__ */ jsx(
|
|
183
297
|
DocsSearch,
|
|
184
298
|
{
|
|
185
|
-
indexUrl:
|
|
299
|
+
indexUrl: `${project.locale ? `/${project.locale.path}` : ""}/rdx-search/${encodeURIComponent(source.routePrefix || "_")}`,
|
|
186
300
|
version: source.id === "current" ? messages.current : formatMessage(messages.version, { version: source.label })
|
|
187
301
|
}
|
|
188
302
|
) : void 0,
|
|
189
303
|
siteTitle: config.metadata.applicationName,
|
|
190
304
|
logo: /* @__PURE__ */ jsx(BrandLogo, { className: "size-5" }),
|
|
191
|
-
homeHref: config.homepage?.enabled ? "/" :
|
|
192
|
-
docsHref:
|
|
305
|
+
homeHref: config.homepage?.enabled ? "/" : getDocsHref3(project),
|
|
306
|
+
docsHref: getDocsHref3(project, source),
|
|
307
|
+
languageMenu: project.localization ? /* @__PURE__ */ jsx(
|
|
308
|
+
LanguageDropdown,
|
|
309
|
+
{
|
|
310
|
+
lang: config.lang,
|
|
311
|
+
current: project.locale?.code ?? project.localization.defaultLocale,
|
|
312
|
+
options: getLanguageLinks(project, source, currentSlug),
|
|
313
|
+
contributionUrl: project.localization.contributionUrl
|
|
314
|
+
}
|
|
315
|
+
) : void 0,
|
|
193
316
|
versionMenu: project.sources.length > 1 ? /* @__PURE__ */ jsx(VersionDropdown, { options, currentId: source.id }) : void 0,
|
|
194
317
|
github: config.navbar?.actions?.github,
|
|
195
318
|
showToggleMode: config.navbar?.actions?.showToggleMode
|
|
@@ -198,7 +321,7 @@ function RdxHeader({
|
|
|
198
321
|
}
|
|
199
322
|
function HomePage({ project }) {
|
|
200
323
|
const { config } = project;
|
|
201
|
-
if (!config.homepage?.enabled) redirect(
|
|
324
|
+
if (!config.homepage?.enabled) redirect(getDocsHref3(project));
|
|
202
325
|
const homepage = config.homepage;
|
|
203
326
|
const CustomHome = homepage.component;
|
|
204
327
|
const BrandLogo = config.navbar?.logo ?? Logo;
|
|
@@ -224,6 +347,25 @@ function HomePage({ project }) {
|
|
|
224
347
|
] });
|
|
225
348
|
}
|
|
226
349
|
function NotFoundPage({ project }) {
|
|
350
|
+
if (project.localization) {
|
|
351
|
+
const { config, localization } = project;
|
|
352
|
+
const BrandLogo = config.navbar?.logo ?? Logo;
|
|
353
|
+
return /* @__PURE__ */ jsx(
|
|
354
|
+
LocalizedNotFound,
|
|
355
|
+
{
|
|
356
|
+
locales: localization.locales,
|
|
357
|
+
defaultLocale: localization.defaultLocale,
|
|
358
|
+
homepage: !!config.homepage?.enabled,
|
|
359
|
+
brand: {
|
|
360
|
+
title: config.navbar?.title,
|
|
361
|
+
siteTitle: config.metadata.applicationName,
|
|
362
|
+
github: config.navbar?.actions?.github,
|
|
363
|
+
showToggleMode: config.navbar?.actions?.showToggleMode,
|
|
364
|
+
logo: /* @__PURE__ */ jsx(BrandLogo, { className: "size-5" })
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
}
|
|
227
369
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
228
370
|
/* @__PURE__ */ jsx(RdxHeader, { project }),
|
|
229
371
|
/* @__PURE__ */ jsx(NotFound, { lang: project.config.lang })
|
|
@@ -233,6 +375,8 @@ async function DocsPage({
|
|
|
233
375
|
slug,
|
|
234
376
|
project
|
|
235
377
|
}) {
|
|
378
|
+
if (project.localization && !project.locale)
|
|
379
|
+
permanentRedirect(getDocsHref3(project, project.sources[0], slug?.join("/")));
|
|
236
380
|
const messages = getMessages(project.config.lang);
|
|
237
381
|
const {
|
|
238
382
|
source,
|
|
@@ -242,11 +386,13 @@ async function DocsPage({
|
|
|
242
386
|
} = resolveDocsRoute2(project, slug);
|
|
243
387
|
if (source.redirectTo !== void 0)
|
|
244
388
|
permanentRedirect(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
389
|
+
getDocsHref3(
|
|
390
|
+
project,
|
|
391
|
+
project.sources.find(
|
|
392
|
+
(entry) => entry.routePrefix === source.redirectTo
|
|
393
|
+
),
|
|
394
|
+
slug?.slice(1).join("/")
|
|
395
|
+
)
|
|
250
396
|
);
|
|
251
397
|
if (!currentSlug)
|
|
252
398
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -257,19 +403,32 @@ async function DocsPage({
|
|
|
257
403
|
] })
|
|
258
404
|
] });
|
|
259
405
|
if (!document) notFound2();
|
|
406
|
+
const fallback = isFallbackPage3(project, source, document.sourcePath);
|
|
407
|
+
const primaryLocale = (source.localization ?? project.localization)?.locales.find(
|
|
408
|
+
(locale) => locale.code === project.localization.defaultLocale
|
|
409
|
+
);
|
|
410
|
+
const originalProject = fallback && primaryLocale ? getLocalizedProject2(project, primaryLocale.path) : project;
|
|
411
|
+
const contentSource = originalProject.sources.find(
|
|
412
|
+
(entry) => entry.id === source.id
|
|
413
|
+
);
|
|
414
|
+
const contentOptions = getSourceOptions4(originalProject, contentSource);
|
|
415
|
+
const contentCategories = fallback ? resolveDocsRoute2(originalProject, slug).categories : categories;
|
|
416
|
+
const contentLanguage = fallback ? primaryLocale.code : project.config.lang;
|
|
260
417
|
const { content } = await compileDocumentation(
|
|
261
418
|
document.content,
|
|
262
419
|
getMdxComponents(project.config.lang),
|
|
263
420
|
{
|
|
264
|
-
sourceRoot:
|
|
421
|
+
sourceRoot: contentOptions.docsDir,
|
|
265
422
|
sourcePath: document.sourcePath,
|
|
266
423
|
routePrefix: source.routePrefix,
|
|
267
|
-
|
|
424
|
+
basePath: contentSource.basePath,
|
|
425
|
+
virtualPaths: !!contentOptions.fallbackDocsDir,
|
|
426
|
+
documents: contentCategories.flatMap((category2) => category2.pages)
|
|
268
427
|
}
|
|
269
428
|
);
|
|
270
429
|
const title = typeof document.frontmatter.title === "string" ? document.frontmatter.title : currentSlug;
|
|
271
430
|
const description = typeof document.frontmatter.description === "string" ? document.frontmatter.description : null;
|
|
272
|
-
const hrefFor = (pageSlug) =>
|
|
431
|
+
const hrefFor = (pageSlug) => getDocsHref3(project, source, pageSlug);
|
|
273
432
|
const sidebarCategories = categories.map((category2) => ({
|
|
274
433
|
title: category2.title,
|
|
275
434
|
links: category2.pages.map((page) => ({
|
|
@@ -315,9 +474,58 @@ async function DocsPage({
|
|
|
315
474
|
"main",
|
|
316
475
|
{
|
|
317
476
|
id: "rdx-main",
|
|
477
|
+
lang: contentLanguage,
|
|
318
478
|
tabIndex: -1,
|
|
319
479
|
className: "w-full flex flex-col gap-5 md:mx-auto md:max-w-[860px] pt-14 md:pt-0 flex-1 outline-none",
|
|
320
480
|
children: [
|
|
481
|
+
fallback && /* @__PURE__ */ jsxs(
|
|
482
|
+
Callout,
|
|
483
|
+
{
|
|
484
|
+
lang: project.config.lang,
|
|
485
|
+
variant: "note",
|
|
486
|
+
title: messages.translationUnavailable,
|
|
487
|
+
"data-rdx-translation-fallback": true,
|
|
488
|
+
children: [
|
|
489
|
+
/* @__PURE__ */ jsx("p", { children: formatMessage(messages.translationFallback, {
|
|
490
|
+
requested: project.locale.label,
|
|
491
|
+
original: primaryLocale.label
|
|
492
|
+
}) }),
|
|
493
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-2", children: [
|
|
494
|
+
/* @__PURE__ */ jsx(
|
|
495
|
+
Button,
|
|
496
|
+
{
|
|
497
|
+
variant: "link",
|
|
498
|
+
href: getDocsHref3(
|
|
499
|
+
originalProject,
|
|
500
|
+
contentSource,
|
|
501
|
+
currentSlug
|
|
502
|
+
),
|
|
503
|
+
children: messages.viewOriginal
|
|
504
|
+
}
|
|
505
|
+
),
|
|
506
|
+
project.localization?.contributionUrl && /* @__PURE__ */ jsxs(
|
|
507
|
+
Button,
|
|
508
|
+
{
|
|
509
|
+
variant: "link",
|
|
510
|
+
href: project.localization.contributionUrl,
|
|
511
|
+
target: "_blank",
|
|
512
|
+
rel: "noopener noreferrer",
|
|
513
|
+
"data-rdx-translate-page": true,
|
|
514
|
+
children: [
|
|
515
|
+
messages.translateThisPage,
|
|
516
|
+
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
517
|
+
" ",
|
|
518
|
+
"(",
|
|
519
|
+
messages.opensInNewTab,
|
|
520
|
+
")"
|
|
521
|
+
] })
|
|
522
|
+
]
|
|
523
|
+
}
|
|
524
|
+
)
|
|
525
|
+
] })
|
|
526
|
+
]
|
|
527
|
+
}
|
|
528
|
+
),
|
|
321
529
|
/* @__PURE__ */ jsxs("header", { className: "mb-6", children: [
|
|
322
530
|
source.badge && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-4", children: source.badge === "Archived" ? messages.archivedVersion : source.badge }),
|
|
323
531
|
/* @__PURE__ */ jsx("span", { className: "text-xs text-zinc-600 font-semibold uppercase block -mb-0.5", children: category?.title }),
|
|
@@ -378,9 +586,13 @@ export {
|
|
|
378
586
|
RdxLayout,
|
|
379
587
|
createRdxProject,
|
|
380
588
|
getDocumentMetadata,
|
|
589
|
+
getLocalizedProject3 as getLocalizedProject,
|
|
381
590
|
getPageMetadata,
|
|
382
591
|
getProjectDocsPaths,
|
|
383
592
|
getRdxMetadata,
|
|
593
|
+
getRdxRobots,
|
|
594
|
+
getRdxSitemap,
|
|
384
595
|
getSearchResponse,
|
|
385
|
-
getSearchSources
|
|
596
|
+
getSearchSources,
|
|
597
|
+
getSourceRobots
|
|
386
598
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lexibase/rdx-next",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "The Next.js runtime for RDX documentation projects.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -48,14 +48,15 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"cross-spawn": "^7.0.6",
|
|
50
50
|
"jiti": "^2.6.1",
|
|
51
|
+
"prompts": "^2.4.2",
|
|
51
52
|
"next-mdx-remote": "^6.0.0",
|
|
52
53
|
"remark-gfm": "^4",
|
|
53
54
|
"typescript": "6.0.3",
|
|
54
|
-
"@lexibase/rdx-core": "1.
|
|
55
|
-
"@lexibase/rdx-types": "1.
|
|
55
|
+
"@lexibase/rdx-core": "2.1.0",
|
|
56
|
+
"@lexibase/rdx-types": "2.1.0"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
|
-
"@lexibase/rdx-ui": "^3.
|
|
59
|
+
"@lexibase/rdx-ui": "^3.3.0",
|
|
59
60
|
"next": "^16.4.2",
|
|
60
61
|
"react": "^19.3.1",
|
|
61
62
|
"react-dom": "^19.3.1"
|
|
@@ -80,7 +81,7 @@
|
|
|
80
81
|
"next": "^16.4.2",
|
|
81
82
|
"react": "19.3.1",
|
|
82
83
|
"react-dom": "19.3.1",
|
|
83
|
-
"@lexibase/rdx-ui": "3.
|
|
84
|
+
"@lexibase/rdx-ui": "3.3.0",
|
|
84
85
|
"@rdx/typescript-config": "1.0.0",
|
|
85
86
|
"@rdx/eslint-config": "1.0.0"
|
|
86
87
|
},
|