@lexibase/rdx-next 3.2.0 → 4.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 +5 -2
- package/bin/cli.mjs +31 -2
- package/dist/runtime/index.d.ts +15 -3
- package/dist/runtime/index.js +60 -10
- package/package.json +6 -6
- package/templates/runtime/app/robots.ts +6 -0
- package/templates/runtime/app/sitemap.ts +6 -0
package/README.md
CHANGED
|
@@ -16,6 +16,10 @@ must still be trusted project-owned content, not untrusted uploads.
|
|
|
16
16
|
It exposes the `rdx` command used by generated projects to create their
|
|
17
17
|
internal `.rdx` application and run Next.js.
|
|
18
18
|
|
|
19
|
+
Run `pnpm rdx check` to validate static documentation links, heading fragments
|
|
20
|
+
and local files in public/ before deployment. It reports source locations and
|
|
21
|
+
exits nonzero for problems, without preparing the runtime or rendering documents.
|
|
22
|
+
|
|
19
23
|
Run `pnpm rdx add versioning` (or `npm exec -- rdx add versioning`) from a
|
|
20
24
|
documentation project to install and activate the official optional plugin.
|
|
21
25
|
The command detects the package manager, preserves config and plugin options,
|
|
@@ -89,7 +93,6 @@ export default defineConfig({
|
|
|
89
93
|
authors: [{ name: 'My team', url: 'https://example.com' }],
|
|
90
94
|
keywords: ['documentation', 'my-project'],
|
|
91
95
|
icons: { icon: '/favicon.ico' },
|
|
92
|
-
robots: { index: true, follow: true },
|
|
93
96
|
openGraph: {
|
|
94
97
|
type: 'website',
|
|
95
98
|
images: [{ url: '/og.png', width: 1200, height: 630, alt: 'My project' }],
|
|
@@ -111,7 +114,7 @@ production domain or force localhost. Image, favicon and manifest paths refer
|
|
|
111
114
|
to files you create in `public/`. Nothing creates those assets automatically.
|
|
112
115
|
|
|
113
116
|
Supported fields are `metadataBase` (an absolute http(s) string),
|
|
114
|
-
`applicationName`, `authors`, `keywords`, `icons`, `manifest`,
|
|
117
|
+
`applicationName`, `authors`, `keywords`, `icons`, `manifest`,
|
|
115
118
|
`openGraph` and `twitter`. TypeScript describes the supported nested options,
|
|
116
119
|
and invalid fields fail during configuration validation. This is a portable
|
|
117
120
|
subset, not an unrestricted pass-through of all Next.js metadata options.
|
package/bin/cli.mjs
CHANGED
|
@@ -50,7 +50,12 @@ export async function runRdxCli({ templateDir } = {}) {
|
|
|
50
50
|
|
|
51
51
|
async function main() {
|
|
52
52
|
if (!command || command === 'help' || command === '--help') {
|
|
53
|
-
console.log(
|
|
53
|
+
console.log(
|
|
54
|
+
'Usage: rdx <dev|build|start|prepare|check|add|plugin-command>'
|
|
55
|
+
)
|
|
56
|
+
console.log(
|
|
57
|
+
' rdx check: validate documentation links, sections and local assets'
|
|
58
|
+
)
|
|
54
59
|
console.log(' rdx add versioning [--package-manager npm|pnpm|yarn]')
|
|
55
60
|
console.log(
|
|
56
61
|
'Plugin commands are registered through plugins in rdx.config.ts.'
|
|
@@ -58,6 +63,19 @@ export async function runRdxCli({ templateDir } = {}) {
|
|
|
58
63
|
return
|
|
59
64
|
}
|
|
60
65
|
|
|
66
|
+
if (command === 'check') {
|
|
67
|
+
if (
|
|
68
|
+
commandArgs.length === 1 &&
|
|
69
|
+
['--help', '-h'].includes(commandArgs[0])
|
|
70
|
+
) {
|
|
71
|
+
console.log(
|
|
72
|
+
'Usage: rdx check\nValidate static MDX links, section anchors and local public/ files without building the site.'
|
|
73
|
+
)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
if (commandArgs.length) throw new Error('Usage: rdx check')
|
|
77
|
+
}
|
|
78
|
+
|
|
61
79
|
if (command === 'add') {
|
|
62
80
|
const { addVersioning, parseAddArguments } =
|
|
63
81
|
await import('./add-plugin.mjs')
|
|
@@ -96,6 +114,17 @@ export async function runRdxCli({ templateDir } = {}) {
|
|
|
96
114
|
rootDir: projectRoot,
|
|
97
115
|
command,
|
|
98
116
|
})
|
|
117
|
+
if (command === 'check') {
|
|
118
|
+
const { checkDocumentation, formatDocumentationCheck } =
|
|
119
|
+
await import('@lexibase/rdx-core/check')
|
|
120
|
+
const report = checkDocumentation(project)
|
|
121
|
+
const output = formatDocumentationCheck(report)
|
|
122
|
+
if (report.diagnostics.length) {
|
|
123
|
+
console.error(output)
|
|
124
|
+
process.exitCode = 1
|
|
125
|
+
} else console.log(output)
|
|
126
|
+
return
|
|
127
|
+
}
|
|
99
128
|
if (command === 'prepare') {
|
|
100
129
|
await prepareRuntime()
|
|
101
130
|
console.log('RDX runtime prepared in .rdx/')
|
|
@@ -107,7 +136,7 @@ export async function runRdxCli({ templateDir } = {}) {
|
|
|
107
136
|
)
|
|
108
137
|
if (!pluginCommand)
|
|
109
138
|
throw new Error(
|
|
110
|
-
`Unknown command "${command}". Available: dev, build, start, prepare, add${project.commands.map((entry) => `, ${entry.name}`).join('')}. Install and configure the plugin that provides this command.`
|
|
139
|
+
`Unknown command "${command}". Available: dev, build, start, prepare, check, add${project.commands.map((entry) => `, ${entry.name}`).join('')}. Install and configure the plugin that provides this command.`
|
|
111
140
|
)
|
|
112
141
|
await pluginCommand.run({
|
|
113
142
|
rootDir: projectRoot,
|
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';
|
|
3
|
+
import { RdxProject, RDXConfig, RdxContentSource, RdxDocPage } from '@lexibase/rdx-types';
|
|
4
4
|
export { createRdxProject, getProjectDocsPaths } from '@lexibase/rdx-core';
|
|
5
|
-
import { Metadata } from 'next';
|
|
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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getDocument as getDocument2,
|
|
4
4
|
getDocsPaths,
|
|
5
|
-
getSourceOptions as
|
|
5
|
+
getSourceOptions as getSourceOptions3,
|
|
6
6
|
resolveDocsRoute as resolveDocsRoute2
|
|
7
7
|
} from "@lexibase/rdx-core";
|
|
8
8
|
import { Button } from "@lexibase/rdx-ui/components/button";
|
|
@@ -63,11 +63,13 @@ function getSearchSources(project) {
|
|
|
63
63
|
return project.config.search ? project.sources.filter((source) => source.redirectTo === void 0).map((source) => ({ source: source.routePrefix || "_" })) : [];
|
|
64
64
|
}
|
|
65
65
|
function getSearchResponse(project, key) {
|
|
66
|
-
|
|
66
|
+
const headers = { "X-Robots-Tag": "noindex, follow" };
|
|
67
|
+
if (!project.config.search)
|
|
68
|
+
return new Response(null, { status: 404, headers });
|
|
67
69
|
const source = project.sources.find(
|
|
68
70
|
(entry) => entry.redirectTo === void 0 && (entry.routePrefix || "_") === key
|
|
69
71
|
);
|
|
70
|
-
if (!source) return new Response(null, { status: 404 });
|
|
72
|
+
if (!source) return new Response(null, { status: 404, headers });
|
|
71
73
|
const options = getSourceOptions(project, source);
|
|
72
74
|
const documents = getDocsIndex(options).flatMap(
|
|
73
75
|
(category) => category.pages.map((page) => ({
|
|
@@ -82,7 +84,47 @@ function getSearchResponse(project, key) {
|
|
|
82
84
|
content: getDocument(page.slug, options)?.content ?? ""
|
|
83
85
|
}))
|
|
84
86
|
);
|
|
85
|
-
return Response.json(buildSearchIndex(documents));
|
|
87
|
+
return Response.json(buildSearchIndex(documents), { headers });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/runtime/indexing.ts
|
|
91
|
+
import { getDocsIndex as getDocsIndex2, getSourceOptions as getSourceOptions2 } from "@lexibase/rdx-core";
|
|
92
|
+
function getSourceRobots(project, source) {
|
|
93
|
+
const published = project.sources.some(
|
|
94
|
+
(entry) => entry.id !== "current" && entry.redirectTo === void 0 && entry.badge !== "Archived"
|
|
95
|
+
);
|
|
96
|
+
return {
|
|
97
|
+
index: source.redirectTo === void 0 && source.badge !== "Archived" && (source.id !== "current" || !published),
|
|
98
|
+
follow: true
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function getRdxRobots(config) {
|
|
102
|
+
return {
|
|
103
|
+
// Do not disallow noindex pages: crawlers must read their HTML directive.
|
|
104
|
+
rules: { userAgent: "*", allow: "/" },
|
|
105
|
+
...config.metadata.metadataBase ? { sitemap: new URL("/sitemap.xml", config.metadata.metadataBase).href } : {}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function getRdxSitemap(project, {
|
|
109
|
+
documentPath = (source, page) => [
|
|
110
|
+
"/docs",
|
|
111
|
+
source.routePrefix,
|
|
112
|
+
page.slug.split("/").map(encodeURIComponent).join("/")
|
|
113
|
+
].filter(Boolean).join("/")
|
|
114
|
+
} = {}) {
|
|
115
|
+
const base = project.config.metadata.metadataBase;
|
|
116
|
+
if (!base) return [];
|
|
117
|
+
const urls = /* @__PURE__ */ new Set();
|
|
118
|
+
if (project.config.homepage?.enabled) urls.add(new URL("/", base).href);
|
|
119
|
+
for (const source of project.sources) {
|
|
120
|
+
if (!getSourceRobots(project, source).index) continue;
|
|
121
|
+
for (const category of getDocsIndex2(getSourceOptions2(project, source))) {
|
|
122
|
+
for (const page of category.pages) {
|
|
123
|
+
urls.add(new URL(documentPath(source, page), base).href);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return [...urls].map((url) => ({ url }));
|
|
86
128
|
}
|
|
87
129
|
|
|
88
130
|
// src/runtime/metadata.ts
|
|
@@ -93,11 +135,15 @@ function getDocumentMetadata({
|
|
|
93
135
|
slug
|
|
94
136
|
}) {
|
|
95
137
|
const route = resolveDocsRoute(project, slug);
|
|
96
|
-
if (route.source.redirectTo !== void 0)
|
|
97
|
-
|
|
138
|
+
if (route.source.redirectTo !== void 0)
|
|
139
|
+
return { robots: getSourceRobots(project, route.source) };
|
|
140
|
+
if (!route.slug) return { robots: { index: false, follow: true } };
|
|
98
141
|
if (!route.document) notFound();
|
|
99
142
|
const page = route.categories.flatMap((category) => category.pages).find((page2) => page2.slug === route.slug);
|
|
100
|
-
return
|
|
143
|
+
return {
|
|
144
|
+
...getPageMetadata(project.config, page),
|
|
145
|
+
robots: getSourceRobots(project, route.source)
|
|
146
|
+
};
|
|
101
147
|
}
|
|
102
148
|
function getPageMetadata(config, page) {
|
|
103
149
|
const title = page.title;
|
|
@@ -117,6 +163,7 @@ function getRdxMetadata(config) {
|
|
|
117
163
|
title: { default: name, template: `%s | ${name}` },
|
|
118
164
|
description: config.description,
|
|
119
165
|
...metadata,
|
|
166
|
+
robots: { index: true, follow: true },
|
|
120
167
|
...metadataBase ? { metadataBase: new URL(metadataBase) } : {},
|
|
121
168
|
...metadata.openGraph ? {
|
|
122
169
|
openGraph: {
|
|
@@ -164,7 +211,7 @@ function RdxHeader({
|
|
|
164
211
|
const messages = getMessages(config.lang);
|
|
165
212
|
const { source, slug: currentSlug } = resolveDocsRoute2(project, slug);
|
|
166
213
|
const options = project.sources.filter((edition) => edition.redirectTo === void 0).map((edition) => {
|
|
167
|
-
const docs =
|
|
214
|
+
const docs = getSourceOptions3(project, edition);
|
|
168
215
|
const destination = currentSlug && getDocument2(currentSlug, docs) ? currentSlug : getDocsPaths(docs)[0];
|
|
169
216
|
return {
|
|
170
217
|
id: edition.id,
|
|
@@ -261,7 +308,7 @@ async function DocsPage({
|
|
|
261
308
|
document.content,
|
|
262
309
|
getMdxComponents(project.config.lang),
|
|
263
310
|
{
|
|
264
|
-
sourceRoot:
|
|
311
|
+
sourceRoot: getSourceOptions3(project, source).docsDir,
|
|
265
312
|
sourcePath: document.sourcePath,
|
|
266
313
|
routePrefix: source.routePrefix,
|
|
267
314
|
documents: categories.flatMap((category2) => category2.pages)
|
|
@@ -381,6 +428,9 @@ export {
|
|
|
381
428
|
getPageMetadata,
|
|
382
429
|
getProjectDocsPaths,
|
|
383
430
|
getRdxMetadata,
|
|
431
|
+
getRdxRobots,
|
|
432
|
+
getRdxSitemap,
|
|
384
433
|
getSearchResponse,
|
|
385
|
-
getSearchSources
|
|
434
|
+
getSearchSources,
|
|
435
|
+
getSourceRobots
|
|
386
436
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lexibase/rdx-next",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "The Next.js runtime for RDX documentation projects.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"next-mdx-remote": "^6.0.0",
|
|
52
52
|
"remark-gfm": "^4",
|
|
53
53
|
"typescript": "6.0.3",
|
|
54
|
-
"@lexibase/rdx-
|
|
55
|
-
"@lexibase/rdx-
|
|
54
|
+
"@lexibase/rdx-types": "2.0.0",
|
|
55
|
+
"@lexibase/rdx-core": "2.0.0"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"@lexibase/rdx-ui": "^3.2.0",
|
|
@@ -80,9 +80,9 @@
|
|
|
80
80
|
"next": "^16.4.2",
|
|
81
81
|
"react": "19.3.1",
|
|
82
82
|
"react-dom": "19.3.1",
|
|
83
|
-
"@
|
|
84
|
-
"@rdx
|
|
85
|
-
"@rdx/
|
|
83
|
+
"@rdx/eslint-config": "1.0.0",
|
|
84
|
+
"@lexibase/rdx-ui": "3.2.1",
|
|
85
|
+
"@rdx/typescript-config": "1.0.0"
|
|
86
86
|
},
|
|
87
87
|
"scripts": {
|
|
88
88
|
"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",
|