@lexibase/rdx-next 1.0.0-beta.0 → 1.0.0-beta.10

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.
@@ -1,77 +1,294 @@
1
1
  // src/runtime/index.tsx
2
- import { getDocument, getDocsIndex, getDocsPaths } from "@lexibase/rdx-core";
2
+ import {
3
+ getDocument as getDocument2,
4
+ getDocsPaths,
5
+ getSourceOptions as getSourceOptions2,
6
+ resolveDocsRoute
7
+ } from "@lexibase/rdx-core";
8
+ import { Button } from "@lexibase/rdx-ui/components/button";
9
+ import { ButtonLink } from "@lexibase/rdx-ui/components/button-link";
10
+ import { DocsSidebar } from "@lexibase/rdx-ui/components/docs-sidebar";
11
+ import { DocsToc } from "@lexibase/rdx-ui/components/docs-toc";
12
+ import { DocsSearch } from "@lexibase/rdx-ui/components/docs-search";
13
+ import { Home } from "@lexibase/rdx-ui/components/home";
14
+ import { Logo } from "@lexibase/rdx-ui/components/logo";
15
+ import { mdxComponents } from "@lexibase/rdx-ui/components/mdx-components";
16
+ import { Navbar } from "@lexibase/rdx-ui/components/navbar";
17
+ import {
18
+ VersionDropdown
19
+ } from "@lexibase/rdx-ui/components/version-dropdown";
20
+ import { ThemeProvider } from "@lexibase/rdx-ui/providers/theme-provider";
21
+ import { notFound, redirect } from "next/navigation";
22
+
23
+ // src/runtime/mdx.ts
3
24
  import { compileMDX } from "next-mdx-remote/rsc";
4
- import Link from "next/link";
5
- import { notFound } from "next/navigation";
6
- var mdxComponents = {
7
- h1: (props) => /* @__PURE__ */ React.createElement("h1", { ...props }),
8
- h2: (props) => /* @__PURE__ */ React.createElement("h2", { ...props }),
9
- h3: (props) => /* @__PURE__ */ React.createElement("h3", { ...props }),
10
- p: (props) => /* @__PURE__ */ React.createElement("p", { ...props }),
11
- ul: (props) => /* @__PURE__ */ React.createElement("ul", { ...props }),
12
- ol: (props) => /* @__PURE__ */ React.createElement("ol", { ...props }),
13
- li: (props) => /* @__PURE__ */ React.createElement("li", { ...props }),
14
- blockquote: (props) => /* @__PURE__ */ React.createElement("blockquote", { ...props }),
15
- pre: (props) => /* @__PURE__ */ React.createElement("pre", { ...props }),
16
- code: (props) => /* @__PURE__ */ React.createElement("code", { ...props }),
17
- a: ({ href, ...props }) => {
18
- const isExternal = href?.startsWith("http");
19
- return /* @__PURE__ */ React.createElement(
20
- "a",
21
- {
22
- href,
23
- rel: isExternal ? "noreferrer" : void 0,
24
- target: isExternal ? "_blank" : void 0,
25
- ...props
25
+ import remarkGfm from "remark-gfm";
26
+ import {
27
+ remarkDocumentLinks,
28
+ rehypeDocumentationHeadings,
29
+ rehypeDocumentationCode
30
+ } from "@lexibase/rdx-core/mdx";
31
+ function compileDocumentation(source, components, links) {
32
+ return compileMDX({
33
+ source,
34
+ components,
35
+ options: {
36
+ blockJS: true,
37
+ blockDangerousJS: true,
38
+ mdxOptions: {
39
+ remarkPlugins: [remarkGfm, [remarkDocumentLinks, links]],
40
+ rehypePlugins: [rehypeDocumentationHeadings, rehypeDocumentationCode]
26
41
  }
27
- );
28
- }
29
- };
42
+ }
43
+ });
44
+ }
45
+
46
+ // src/runtime/index.tsx
47
+ import { createRdxProject, getProjectDocsPaths } from "@lexibase/rdx-core";
48
+
49
+ // src/runtime/search.ts
50
+ import { getDocsIndex, getDocument, getSourceOptions } from "@lexibase/rdx-core";
51
+ import { buildSearchIndex } from "@lexibase/rdx-core/search";
52
+ function getSearchSources(project) {
53
+ return project.config.search ? project.sources.map((source) => ({ source: source.routePrefix || "_" })) : [];
54
+ }
55
+ function getSearchResponse(project, key) {
56
+ if (!project.config.search) return new Response(null, { status: 404 });
57
+ const source = project.sources.find(
58
+ (entry) => (entry.routePrefix || "_") === key
59
+ );
60
+ if (!source) return new Response(null, { status: 404 });
61
+ const options = getSourceOptions(project, source);
62
+ const documents = getDocsIndex(options).flatMap(
63
+ (category) => category.pages.map((page) => ({
64
+ title: page.title,
65
+ description: page.description,
66
+ category: category.title,
67
+ href: [
68
+ "/docs",
69
+ source.routePrefix,
70
+ page.slug.split("/").map(encodeURIComponent).join("/")
71
+ ].filter(Boolean).join("/"),
72
+ content: getDocument(page.slug, options)?.content ?? ""
73
+ }))
74
+ );
75
+ return Response.json(buildSearchIndex(documents));
76
+ }
77
+
78
+ // src/runtime/index.tsx
79
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
30
80
  function RdxLayout({
31
81
  children,
32
- config
82
+ config,
83
+ bodyClassName
33
84
  }) {
34
- const brand = config.navbar?.title || config.title;
35
- return /* @__PURE__ */ React.createElement("html", { lang: "en" }, /* @__PURE__ */ React.createElement("body", null, /* @__PURE__ */ React.createElement("header", { className: "rdx-header" }, /* @__PURE__ */ React.createElement(Link, { className: "rdx-brand", href: "/docs" }, brand), config.navbar?.actions?.github?.visible && config.navbar.actions.github.url && /* @__PURE__ */ React.createElement(
36
- "a",
85
+ return /* @__PURE__ */ jsx("html", { suppressHydrationWarning: true, lang: config.lang ?? "en", children: /* @__PURE__ */ jsx("body", { className: bodyClassName, children: /* @__PURE__ */ jsxs(
86
+ ThemeProvider,
37
87
  {
38
- className: "rdx-github-link",
39
- href: config.navbar.actions.github.url,
40
- rel: "noreferrer",
41
- target: "_blank"
42
- },
43
- "GitHub"
44
- )), children));
88
+ disableTransitionOnChange: true,
89
+ enableSystem: true,
90
+ attribute: "class",
91
+ defaultTheme: config.theme?.defaultTheme ?? "system",
92
+ children: [
93
+ /* @__PURE__ */ jsx("a", { className: "rdx-skip-link", href: "#rdx-main", children: "Skip to content" }),
94
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col h-dvh", children })
95
+ ]
96
+ }
97
+ ) }) });
45
98
  }
46
- async function DocsPage({ slug }) {
47
- const categories = getDocsIndex();
48
- const currentSlug = slug?.join("/") || getDocsPaths()[0];
49
- if (!currentSlug) {
50
- return /* @__PURE__ */ React.createElement("main", { className: "rdx-empty-state" }, /* @__PURE__ */ React.createElement("h1", null, "Your documentation is ready."), /* @__PURE__ */ React.createElement("p", null, "Add an `.mdx` file inside `docs/` to create your first page."));
51
- }
52
- const document = getDocument(currentSlug);
53
- if (!document) notFound();
54
- const { content } = await compileMDX({
55
- source: document.content,
56
- components: mdxComponents
99
+ function RdxHeader({
100
+ project,
101
+ slug
102
+ }) {
103
+ const { config } = project;
104
+ const { source, slug: currentSlug } = resolveDocsRoute(project, slug);
105
+ const options = project.sources.map((edition) => {
106
+ const docs = getSourceOptions2(project, edition);
107
+ const destination = currentSlug && getDocument2(currentSlug, docs) ? currentSlug : getDocsPaths(docs)[0];
108
+ return {
109
+ id: edition.id,
110
+ label: edition.id === "current" ? "Current" : `Version ${edition.label}`,
111
+ kind: edition.id === "current" ? "live" : edition.badge === "Archived" ? "archived" : "version",
112
+ href: ["/docs", edition.routePrefix, destination].filter(Boolean).join("/")
113
+ };
57
114
  });
115
+ const BrandLogo = config.navbar?.logo ?? Logo;
116
+ return /* @__PURE__ */ jsx(
117
+ Navbar,
118
+ {
119
+ title: config.navbar?.title ?? "",
120
+ search: config.search ? /* @__PURE__ */ jsx(
121
+ DocsSearch,
122
+ {
123
+ indexUrl: `/rdx-search/${encodeURIComponent(source.routePrefix || "_")}`,
124
+ version: source.label
125
+ }
126
+ ) : void 0,
127
+ siteTitle: config.title,
128
+ logo: /* @__PURE__ */ jsx(BrandLogo, { className: "size-5" }),
129
+ homeHref: config.homepage?.enabled ? "/" : "/docs",
130
+ docsHref: ["/docs", source.routePrefix].filter(Boolean).join("/"),
131
+ versionMenu: project.sources.length > 1 ? /* @__PURE__ */ jsx(VersionDropdown, { options, currentId: source.id }) : void 0,
132
+ github: config.navbar?.actions?.github,
133
+ showToggleMode: config.navbar?.actions?.showToggleMode
134
+ }
135
+ );
136
+ }
137
+ function HomePage({ project }) {
138
+ const { config } = project;
139
+ if (!config.homepage?.enabled) redirect("/docs");
140
+ const homepage = config.homepage;
141
+ const CustomHome = homepage.component;
142
+ const BrandLogo = config.navbar?.logo ?? Logo;
143
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
144
+ /* @__PURE__ */ jsx(RdxHeader, { project }),
145
+ /* @__PURE__ */ jsx(
146
+ "main",
147
+ {
148
+ id: "rdx-main",
149
+ tabIndex: -1,
150
+ className: "flex-1 min-h-0 overflow-y-auto",
151
+ children: CustomHome ? /* @__PURE__ */ jsx(CustomHome, {}) : /* @__PURE__ */ jsx(
152
+ Home,
153
+ {
154
+ title: homepage.title ?? config.title,
155
+ logo: /* @__PURE__ */ jsx(BrandLogo, { className: "size-52 hover:scale-105 hover:rotate-1 transition-[scale,rotation] duration-1000" }),
156
+ description: homepage.description,
157
+ buttons: homepage.buttons
158
+ }
159
+ )
160
+ }
161
+ )
162
+ ] });
163
+ }
164
+ async function DocsPage({
165
+ slug,
166
+ project
167
+ }) {
168
+ const {
169
+ source,
170
+ slug: currentSlug,
171
+ document,
172
+ categories
173
+ } = resolveDocsRoute(project, slug);
174
+ if (!currentSlug)
175
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
176
+ /* @__PURE__ */ jsx(RdxHeader, { project, slug }),
177
+ /* @__PURE__ */ jsxs("main", { id: "rdx-main", tabIndex: -1, className: "p-8", children: [
178
+ /* @__PURE__ */ jsx("h1", { children: "Your documentation is ready." }),
179
+ /* @__PURE__ */ jsx("p", { children: "Add an .mdx file inside your configured documentation directory." })
180
+ ] })
181
+ ] });
182
+ if (!document) notFound();
183
+ const { content } = await compileDocumentation(
184
+ document.content,
185
+ mdxComponents,
186
+ {
187
+ sourceRoot: getSourceOptions2(project, source).docsDir,
188
+ sourcePath: document.sourcePath,
189
+ routePrefix: source.routePrefix,
190
+ documents: categories.flatMap((category2) => category2.pages)
191
+ }
192
+ );
58
193
  const title = typeof document.frontmatter.title === "string" ? document.frontmatter.title : currentSlug;
59
194
  const description = typeof document.frontmatter.description === "string" ? document.frontmatter.description : null;
60
- return /* @__PURE__ */ React.createElement("div", { className: "rdx-docs-layout" }, /* @__PURE__ */ React.createElement("aside", { className: "rdx-sidebar", "aria-label": "Documentation navigation" }, categories.map((category) => /* @__PURE__ */ React.createElement("section", { key: category.id, className: "rdx-sidebar-category" }, /* @__PURE__ */ React.createElement("h2", null, category.title), /* @__PURE__ */ React.createElement("ul", null, category.pages.map((page) => {
61
- const href = `/docs/${page.slug}`;
62
- const isCurrentPage = page.slug === currentSlug;
63
- return /* @__PURE__ */ React.createElement("li", { key: page.slug }, /* @__PURE__ */ React.createElement(
64
- Link,
65
- {
66
- "aria-current": isCurrentPage ? "page" : void 0,
67
- className: isCurrentPage ? "is-active" : void 0,
68
- href
69
- },
70
- page.title
71
- ));
72
- }))))), /* @__PURE__ */ React.createElement("main", { className: "rdx-document" }, /* @__PURE__ */ React.createElement("header", { className: "rdx-document-header" }, /* @__PURE__ */ React.createElement("p", { className: "rdx-breadcrumb" }, currentSlug), /* @__PURE__ */ React.createElement("h1", null, title), description && /* @__PURE__ */ React.createElement("p", { className: "rdx-description" }, description)), /* @__PURE__ */ React.createElement("article", { className: "rdx-mdx-content" }, content)));
195
+ const hrefFor = (pageSlug) => ["/docs", source.routePrefix, pageSlug].filter(Boolean).join("/");
196
+ const sidebarCategories = categories.map((category2) => ({
197
+ title: category2.title,
198
+ links: category2.pages.map((page) => ({
199
+ label: page.title,
200
+ href: hrefFor(page.slug)
201
+ }))
202
+ }));
203
+ const pages = categories.flatMap((category2) => category2.pages);
204
+ const index = pages.findIndex((page) => page.slug === currentSlug);
205
+ const previous = pages[index - 1];
206
+ const next = pages[index + 1];
207
+ const category = categories.find(
208
+ (entry) => entry.pages.some((page) => page.slug === currentSlug)
209
+ );
210
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
211
+ /* @__PURE__ */ jsx(RdxHeader, { project, slug }),
212
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 min-h-0 overflow-hidden relative", children: [
213
+ /* @__PURE__ */ jsx(
214
+ DocsSidebar,
215
+ {
216
+ categories: sidebarCategories,
217
+ pathname: hrefFor(currentSlug)
218
+ }
219
+ ),
220
+ /* @__PURE__ */ jsxs(
221
+ "div",
222
+ {
223
+ "data-rdx-docs-scroll": true,
224
+ className: "relative flex-1 min-w-0 h-full overflow-y-auto pt-12 md:pt-0 outline-none",
225
+ children: [
226
+ project.config.TOC && /* @__PURE__ */ jsx(DocsToc, {}, hrefFor(currentSlug)),
227
+ /* @__PURE__ */ jsxs(
228
+ "section",
229
+ {
230
+ className: `w-full min-h-full px-7 flex flex-col gap-5 pb-8 md:py-8 ${project.config.TOC ? "xl:pr-20" : ""}`,
231
+ children: [
232
+ /* @__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: [
233
+ /* @__PURE__ */ jsxs("header", { className: "mb-6", children: [
234
+ source.badge && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-4", children: source.badge }),
235
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-zinc-600 font-semibold uppercase block -mb-0.5", children: category?.title }),
236
+ /* @__PURE__ */ jsx("h1", { className: "text-4xl uppercase font-bold mb-2", children: title }),
237
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-ring max-w-[726px]", children: description })
238
+ ] }),
239
+ /* @__PURE__ */ jsx(
240
+ "main",
241
+ {
242
+ id: "rdx-main",
243
+ tabIndex: -1,
244
+ className: "h-full w-full flex-1 max-w-[960px]",
245
+ children: content
246
+ }
247
+ )
248
+ ] }),
249
+ /* @__PURE__ */ jsxs("footer", { className: "flex flex-col", children: [
250
+ /* @__PURE__ */ jsxs("section", { className: "flex items-center justify-between", children: [
251
+ previous ? /* @__PURE__ */ jsx(
252
+ ButtonLink,
253
+ {
254
+ variant: "outline",
255
+ size: "sm",
256
+ href: hrefFor(previous.slug),
257
+ children: previous.title
258
+ }
259
+ ) : /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", disabled: true, children: "Previous" }),
260
+ next ? /* @__PURE__ */ jsx(
261
+ ButtonLink,
262
+ {
263
+ variant: "outline",
264
+ size: "sm",
265
+ href: hrefFor(next.slug),
266
+ children: next.title
267
+ }
268
+ ) : /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", disabled: true, children: "Next" })
269
+ ] }),
270
+ /* @__PURE__ */ jsx("section", { className: "flex items-center justify-center w-full py-5", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
271
+ "\xA9 ",
272
+ (/* @__PURE__ */ new Date()).getFullYear(),
273
+ " ",
274
+ /* @__PURE__ */ jsx("strong", { children: project.config.title })
275
+ ] }) })
276
+ ] })
277
+ ]
278
+ }
279
+ )
280
+ ]
281
+ }
282
+ )
283
+ ] })
284
+ ] });
73
285
  }
74
286
  export {
75
287
  DocsPage,
76
- RdxLayout
288
+ HomePage,
289
+ RdxLayout,
290
+ createRdxProject,
291
+ getProjectDocsPaths,
292
+ getSearchResponse,
293
+ getSearchSources
77
294
  };
package/package.json CHANGED
@@ -1,13 +1,21 @@
1
1
  {
2
2
  "name": "@lexibase/rdx-next",
3
- "version": "1.0.0-beta.0",
3
+ "version": "1.0.0-beta.10",
4
4
  "description": "The Next.js runtime for RDX documentation projects.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
+ "engines": {
8
+ "node": ">=20.9"
9
+ },
7
10
  "bin": {
8
11
  "rdx": "./bin/rdx.mjs"
9
12
  },
10
13
  "exports": {
14
+ "./postcss.config": "./postcss.config.mjs",
15
+ "./styles.css": {
16
+ "types": "./src/runtime/styles.d.css.ts",
17
+ "default": "./src/runtime/styles.css"
18
+ },
11
19
  ".": {
12
20
  "types": "./dist/index.d.ts",
13
21
  "import": "./dist/index.js"
@@ -21,17 +29,25 @@
21
29
  "bin",
22
30
  "dist",
23
31
  "templates",
32
+ "postcss.config.mjs",
33
+ "src/runtime/styles.css",
34
+ "src/runtime/styles.d.css.ts",
24
35
  "README.md"
25
36
  ],
26
37
  "dependencies": {
27
- "next-mdx-remote": "^5.0.0",
28
- "@lexibase/rdx-types": "1.0.0-beta.0",
29
- "@lexibase/rdx-core": "1.0.0-beta.0"
38
+ "cross-spawn": "^7.0.6",
39
+ "jiti": "^2.6.1",
40
+ "next-mdx-remote": "^6.0.0",
41
+ "remark-gfm": "^4",
42
+ "typescript": "6.0.3",
43
+ "@lexibase/rdx-core": "1.0.0-beta.7",
44
+ "@lexibase/rdx-ui": "1.0.0-beta.2",
45
+ "@lexibase/rdx-types": "1.0.0-beta.5"
30
46
  },
31
47
  "peerDependencies": {
32
- "next": "^15.5.0",
33
- "react": "^19.1.0",
34
- "react-dom": "^19.1.0"
48
+ "next": "^16.4.2",
49
+ "react": "^19.3.1",
50
+ "react-dom": "^19.3.1"
35
51
  },
36
52
  "publishConfig": {
37
53
  "access": "public"
@@ -47,19 +63,20 @@
47
63
  "homepage": "https://github.com/lexibase/rdx#readme",
48
64
  "devDependencies": {
49
65
  "@types/node": "^22.15.3",
50
- "@types/react": "19.1.0",
51
- "@types/react-dom": "19.1.1",
52
- "eslint": "^9.34.0",
53
- "next": "^15.5.0",
54
- "react": "19.1.0",
55
- "react-dom": "19.1.0",
56
- "typescript": "5.9.2",
57
- "@rdx/eslint-config": "1.0.0",
58
- "@rdx/typescript-config": "1.0.0"
66
+ "@types/react": "19.3.0",
67
+ "@types/react-dom": "19.3.0",
68
+ "eslint": "^9.39.5",
69
+ "next": "^16.4.2",
70
+ "react": "19.3.1",
71
+ "react-dom": "19.3.1",
72
+ "@rdx/typescript-config": "1.0.0",
73
+ "@rdx/eslint-config": "1.0.0"
59
74
  },
60
75
  "scripts": {
61
76
  "build": "tsup src/index.ts src/runtime/index.tsx --format esm --dts --clean --external @lexibase/rdx-core --external @lexibase/rdx-types --external next --external react --external react-dom --external next-mdx-remote",
62
77
  "lint": "eslint . --max-warnings 0",
63
- "check-types": "tsc --noEmit"
78
+ "check-types": "tsc --noEmit",
79
+ "test": "node --test tests/*.test.mjs",
80
+ "test:runtime": "node --test tests/runtime.integration.mjs"
64
81
  }
65
82
  }
@@ -0,0 +1 @@
1
+ export { default } from '@lexibase/rdx-ui/postcss.config'
@@ -0,0 +1,16 @@
1
+ @import '@lexibase/rdx-ui/globals.css';
2
+ @source '../../dist/**/*.js';
3
+
4
+ /* Colors, buttons, dropdowns and typography come from the shared UI theme. */
5
+ .rdx-skip-link {
6
+ position: fixed;
7
+ top: -100px;
8
+ left: 16px;
9
+ z-index: 100;
10
+ padding: 12px;
11
+ background: var(--background);
12
+ color: var(--foreground);
13
+ }
14
+ .rdx-skip-link:focus {
15
+ top: 12px;
16
+ }
@@ -0,0 +1,2 @@
1
+ // Global runtime styles are loaded for their side effects only.
2
+ export {}
@@ -1,8 +1,11 @@
1
- import { DocsPage } from '@lexibase/rdx-next/runtime'
2
- import { getDocsPaths } from '@lexibase/rdx-core'
1
+ import { DocsPage, getProjectDocsPaths } from '@lexibase/rdx-next/runtime'
3
2
 
4
- export function generateStaticParams() {
5
- return getDocsPaths().map((slug) => ({ slug: slug.split('/') }))
3
+ import { getProject } from '../../../rdx-project'
4
+
5
+ export async function generateStaticParams() {
6
+ return getProjectDocsPaths(await getProject()).map((slug) => ({
7
+ slug: slug.split('/'),
8
+ }))
6
9
  }
7
10
 
8
11
  export default async function DocumentationPage({
@@ -12,5 +15,5 @@ export default async function DocumentationPage({
12
15
  }) {
13
16
  const { slug } = await params
14
17
 
15
- return <DocsPage slug={slug} />
18
+ return <DocsPage slug={slug} project={await getProject()} />
16
19
  }