@lexibase/rdx-next 3.1.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 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`, `robots`,
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('Usage: rdx <dev|build|start|prepare|add|plugin-command>')
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/config.d.mts CHANGED
@@ -1,8 +1,13 @@
1
1
  /** Common build root for the documentation project and its installed Next.js. */
2
2
  export function getRdxBuildRoot(rootDir: string): Promise<string>
3
3
 
4
- /** Explicit deployment files for a native Next.js application using RDX. */
4
+ /** Explicit deployment files and historical route redirects for a native Next.js application. */
5
5
  export function getRdxTracingConfig(options: {
6
6
  rootDir: string
7
7
  appDir?: string
8
- }): Promise<{ outputFileTracingIncludes: Record<string, string[]> }>
8
+ }): Promise<{
9
+ outputFileTracingIncludes: Record<string, string[]>
10
+ redirects?: () => Promise<
11
+ { source: string; destination: string; permanent: boolean }[]
12
+ >
13
+ }>
package/config.mjs CHANGED
@@ -62,7 +62,7 @@ function literalPattern(filename) {
62
62
  .replace(/([*?[\]{}()!+@])/g, '\\$1')
63
63
  }
64
64
 
65
- /** Next.js tracing for configured RDX content, independent of plugin names. */
65
+ /** Next.js tracing and historical route redirects, independent of plugin names. */
66
66
  export async function getRdxTracingConfig({ rootDir, appDir = rootDir }) {
67
67
  rootDir = path.resolve(rootDir)
68
68
  appDir = path.resolve(appDir)
@@ -77,11 +77,17 @@ export async function getRdxTracingConfig({ rootDir, appDir = rootDir }) {
77
77
  const project = await createRdxProject(input, { rootDir })
78
78
  const files = (
79
79
  await Promise.all(
80
- project.sources.map((source) =>
81
- contentFiles(
82
- resolveProjectPath(rootDir, source.docsDir, 'content source docsDir')
80
+ project.sources
81
+ .filter((source) => source.redirectTo === undefined)
82
+ .map((source) =>
83
+ contentFiles(
84
+ resolveProjectPath(
85
+ rootDir,
86
+ source.docsDir,
87
+ 'content source docsDir'
88
+ )
89
+ )
83
90
  )
84
- )
85
91
  )
86
92
  ).flat()
87
93
  for (const filename of project.runtimeFiles) {
@@ -94,7 +100,15 @@ export async function getRdxTracingConfig({ rootDir, appDir = rootDir }) {
94
100
  throw new Error(`Plugin runtimeFiles must name a file: ${filename}`)
95
101
  files.push(resolved)
96
102
  }
103
+ const redirects = project.sources
104
+ .filter((source) => source.redirectTo !== undefined)
105
+ .map((source) => ({
106
+ source: `/docs/${source.routePrefix}/:path*`,
107
+ destination: `/docs/${source.redirectTo}/:path*`,
108
+ permanent: true,
109
+ }))
97
110
  return {
111
+ ...(redirects.length ? { redirects: async () => redirects } : {}),
98
112
  outputFileTracingIncludes: {
99
113
  '/*': [
100
114
  ...new Set(
@@ -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 };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getDocument as getDocument2,
4
4
  getDocsPaths,
5
- getSourceOptions as getSourceOptions2,
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";
@@ -11,6 +11,7 @@ import { DocsToc } from "@lexibase/rdx-ui/components/docs-toc";
11
11
  import { DocsPageTransition } from "@lexibase/rdx-ui/components/docs-page-transition";
12
12
  import { DocsSearch } from "@lexibase/rdx-ui/components/docs-search";
13
13
  import { Home } from "@lexibase/rdx-ui/components/home";
14
+ import { SkipLink } from "@lexibase/rdx-ui/components/skip-link";
14
15
  import { FooterCredit } from "@lexibase/rdx-ui/components/footer-credit";
15
16
  import { Logo } from "@lexibase/rdx-ui/components/logo";
16
17
  import { getMdxComponents } from "@lexibase/rdx-ui/components/mdx-components";
@@ -22,7 +23,7 @@ import {
22
23
  VersionDropdown
23
24
  } from "@lexibase/rdx-ui/components/version-dropdown";
24
25
  import { ThemeProvider } from "@lexibase/rdx-ui/providers/theme-provider";
25
- import { notFound as notFound2, redirect } from "next/navigation";
26
+ import { notFound as notFound2, redirect, permanentRedirect } from "next/navigation";
26
27
 
27
28
  // src/runtime/mdx.ts
28
29
  import { compileMDX } from "next-mdx-remote/rsc";
@@ -59,14 +60,16 @@ import { createRdxProject, getProjectDocsPaths } from "@lexibase/rdx-core";
59
60
  import { getDocsIndex, getDocument, getSourceOptions } from "@lexibase/rdx-core";
60
61
  import { buildSearchIndex } from "@lexibase/rdx-core/search";
61
62
  function getSearchSources(project) {
62
- return project.config.search ? project.sources.map((source) => ({ source: source.routePrefix || "_" })) : [];
63
+ return project.config.search ? project.sources.filter((source) => source.redirectTo === void 0).map((source) => ({ source: source.routePrefix || "_" })) : [];
63
64
  }
64
65
  function getSearchResponse(project, key) {
65
- if (!project.config.search) return new Response(null, { status: 404 });
66
+ const headers = { "X-Robots-Tag": "noindex, follow" };
67
+ if (!project.config.search)
68
+ return new Response(null, { status: 404, headers });
66
69
  const source = project.sources.find(
67
- (entry) => (entry.routePrefix || "_") === key
70
+ (entry) => entry.redirectTo === void 0 && (entry.routePrefix || "_") === key
68
71
  );
69
- if (!source) return new Response(null, { status: 404 });
72
+ if (!source) return new Response(null, { status: 404, headers });
70
73
  const options = getSourceOptions(project, source);
71
74
  const documents = getDocsIndex(options).flatMap(
72
75
  (category) => category.pages.map((page) => ({
@@ -81,7 +84,47 @@ function getSearchResponse(project, key) {
81
84
  content: getDocument(page.slug, options)?.content ?? ""
82
85
  }))
83
86
  );
84
- 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 }));
85
128
  }
86
129
 
87
130
  // src/runtime/metadata.ts
@@ -92,10 +135,15 @@ function getDocumentMetadata({
92
135
  slug
93
136
  }) {
94
137
  const route = resolveDocsRoute(project, slug);
95
- if (!route.slug) return {};
138
+ if (route.source.redirectTo !== void 0)
139
+ return { robots: getSourceRobots(project, route.source) };
140
+ if (!route.slug) return { robots: { index: false, follow: true } };
96
141
  if (!route.document) notFound();
97
142
  const page = route.categories.flatMap((category) => category.pages).find((page2) => page2.slug === route.slug);
98
- return getPageMetadata(project.config, page);
143
+ return {
144
+ ...getPageMetadata(project.config, page),
145
+ robots: getSourceRobots(project, route.source)
146
+ };
99
147
  }
100
148
  function getPageMetadata(config, page) {
101
149
  const title = page.title;
@@ -115,6 +163,7 @@ function getRdxMetadata(config) {
115
163
  title: { default: name, template: `%s | ${name}` },
116
164
  description: config.description,
117
165
  ...metadata,
166
+ robots: { index: true, follow: true },
118
167
  ...metadataBase ? { metadataBase: new URL(metadataBase) } : {},
119
168
  ...metadata.openGraph ? {
120
169
  openGraph: {
@@ -140,7 +189,6 @@ function RdxLayout({
140
189
  config,
141
190
  bodyClassName
142
191
  }) {
143
- const messages = getMessages(config.lang);
144
192
  return /* @__PURE__ */ jsx("html", { suppressHydrationWarning: true, lang: config.lang ?? "en", children: /* @__PURE__ */ jsx("body", { className: bodyClassName, children: /* @__PURE__ */ jsx(
145
193
  ThemeProvider,
146
194
  {
@@ -149,7 +197,7 @@ function RdxLayout({
149
197
  attribute: "class",
150
198
  defaultTheme: config.theme?.defaultTheme ?? "system",
151
199
  children: /* @__PURE__ */ jsxs(LocaleProvider, { lang: config.lang, children: [
152
- /* @__PURE__ */ jsx("a", { className: "rdx-skip-link", href: "#rdx-main", children: messages.skipContent }),
200
+ /* @__PURE__ */ jsx(SkipLink, { lang: config.lang }),
153
201
  /* @__PURE__ */ jsx("div", { className: "flex flex-col h-dvh", children })
154
202
  ] })
155
203
  }
@@ -162,8 +210,8 @@ function RdxHeader({
162
210
  const { config } = project;
163
211
  const messages = getMessages(config.lang);
164
212
  const { source, slug: currentSlug } = resolveDocsRoute2(project, slug);
165
- const options = project.sources.map((edition) => {
166
- const docs = getSourceOptions2(project, edition);
213
+ const options = project.sources.filter((edition) => edition.redirectTo === void 0).map((edition) => {
214
+ const docs = getSourceOptions3(project, edition);
167
215
  const destination = currentSlug && getDocument2(currentSlug, docs) ? currentSlug : getDocsPaths(docs)[0];
168
216
  return {
169
217
  id: edition.id,
@@ -239,6 +287,14 @@ async function DocsPage({
239
287
  document,
240
288
  categories
241
289
  } = resolveDocsRoute2(project, slug);
290
+ if (source.redirectTo !== void 0)
291
+ permanentRedirect(
292
+ [
293
+ "/docs",
294
+ source.redirectTo,
295
+ ...slug?.slice(1).map(encodeURIComponent) ?? []
296
+ ].join("/")
297
+ );
242
298
  if (!currentSlug)
243
299
  return /* @__PURE__ */ jsxs(Fragment, { children: [
244
300
  /* @__PURE__ */ jsx(RdxHeader, { project, slug }),
@@ -252,7 +308,7 @@ async function DocsPage({
252
308
  document.content,
253
309
  getMdxComponents(project.config.lang),
254
310
  {
255
- sourceRoot: getSourceOptions2(project, source).docsDir,
311
+ sourceRoot: getSourceOptions3(project, source).docsDir,
256
312
  sourcePath: document.sourcePath,
257
313
  routePrefix: source.routePrefix,
258
314
  documents: categories.flatMap((category2) => category2.pages)
@@ -302,23 +358,23 @@ async function DocsPage({
302
358
  {
303
359
  className: `w-full min-h-full bg-background px-7 flex flex-col gap-5 pb-8 md:py-8 ${project.config.TOC ? "xl:pr-20" : ""}`,
304
360
  children: [
305
- /* @__PURE__ */ jsxs("div", { className: "w-full flex flex-col gap-5 md:mx-auto md:max-w-[860px] pt-14 md:pt-0 flex-1", children: [
306
- /* @__PURE__ */ jsxs("header", { className: "mb-6", children: [
307
- source.badge && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-4", children: source.badge === "Archived" ? messages.archivedVersion : source.badge }),
308
- /* @__PURE__ */ jsx("span", { className: "text-xs text-zinc-600 font-semibold uppercase block -mb-0.5", children: category?.title }),
309
- /* @__PURE__ */ jsx("h1", { className: "text-4xl uppercase font-bold mb-2", children: title }),
310
- description && /* @__PURE__ */ jsx("p", { className: "text-sm text-ring max-w-[726px]", children: description })
311
- ] }),
312
- /* @__PURE__ */ jsx(
313
- "main",
314
- {
315
- id: "rdx-main",
316
- tabIndex: -1,
317
- className: "h-full w-full flex-1 max-w-[960px]",
318
- children: content
319
- }
320
- )
321
- ] }),
361
+ /* @__PURE__ */ jsxs(
362
+ "main",
363
+ {
364
+ id: "rdx-main",
365
+ tabIndex: -1,
366
+ className: "w-full flex flex-col gap-5 md:mx-auto md:max-w-[860px] pt-14 md:pt-0 flex-1 outline-none",
367
+ children: [
368
+ /* @__PURE__ */ jsxs("header", { className: "mb-6", children: [
369
+ source.badge && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-4", children: source.badge === "Archived" ? messages.archivedVersion : source.badge }),
370
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-zinc-600 font-semibold uppercase block -mb-0.5", children: category?.title }),
371
+ /* @__PURE__ */ jsx("h1", { className: "text-4xl uppercase font-bold mb-2", children: title }),
372
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-ring max-w-[726px]", children: description })
373
+ ] }),
374
+ /* @__PURE__ */ jsx("div", { className: "h-full w-full flex-1 max-w-[960px]", children: content })
375
+ ]
376
+ }
377
+ ),
322
378
  /* @__PURE__ */ jsxs("footer", { className: "flex flex-col", children: [
323
379
  /* @__PURE__ */ jsxs("section", { className: "flex items-center justify-between", children: [
324
380
  previous ? /* @__PURE__ */ jsx(
@@ -372,6 +428,9 @@ export {
372
428
  getPageMetadata,
373
429
  getProjectDocsPaths,
374
430
  getRdxMetadata,
431
+ getRdxRobots,
432
+ getRdxSitemap,
375
433
  getSearchResponse,
376
- getSearchSources
434
+ getSearchSources,
435
+ getSourceRobots
377
436
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lexibase/rdx-next",
3
- "version": "3.1.0",
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,11 +51,11 @@
51
51
  "next-mdx-remote": "^6.0.0",
52
52
  "remark-gfm": "^4",
53
53
  "typescript": "6.0.3",
54
- "@lexibase/rdx-types": "1.1.1",
55
- "@lexibase/rdx-core": "1.3.0"
54
+ "@lexibase/rdx-types": "2.0.0",
55
+ "@lexibase/rdx-core": "2.0.0"
56
56
  },
57
57
  "peerDependencies": {
58
- "@lexibase/rdx-ui": "^3.1.0",
58
+ "@lexibase/rdx-ui": "^3.2.0",
59
59
  "next": "^16.4.2",
60
60
  "react": "^19.3.1",
61
61
  "react-dom": "^19.3.1"
@@ -81,7 +81,7 @@
81
81
  "react": "19.3.1",
82
82
  "react-dom": "19.3.1",
83
83
  "@rdx/eslint-config": "1.0.0",
84
- "@lexibase/rdx-ui": "3.1.0",
84
+ "@lexibase/rdx-ui": "3.2.1",
85
85
  "@rdx/typescript-config": "1.0.0"
86
86
  },
87
87
  "scripts": {
@@ -2,16 +2,4 @@
2
2
  /* Register the entry file directly: consumer ignore rules can hide nested dist folders. */
3
3
  @source '../../dist/runtime/index.js';
4
4
 
5
- /* Colors, buttons, dropdowns and typography come from the shared UI theme. */
6
- .rdx-skip-link {
7
- position: fixed;
8
- top: -100px;
9
- left: 16px;
10
- z-index: 100;
11
- padding: 12px;
12
- background: var(--background);
13
- color: var(--foreground);
14
- }
15
- .rdx-skip-link:focus {
16
- top: 12px;
17
- }
5
+ /* Colors, focus navigation and typography come from the shared UI theme. */
@@ -0,0 +1,6 @@
1
+ import { getRdxRobots } from '@lexibase/rdx-next/runtime'
2
+ import { getProject } from '../rdx-project'
3
+
4
+ export default async function robots() {
5
+ return getRdxRobots((await getProject()).config)
6
+ }
@@ -0,0 +1,6 @@
1
+ import { getRdxSitemap } from '@lexibase/rdx-next/runtime'
2
+ import { getProject } from '../rdx-project'
3
+
4
+ export default async function sitemap() {
5
+ return getRdxSitemap(await getProject())
6
+ }