@xyd-js/cli 0.1.0-xyd.18 → 0.1.0-xyd.2

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.
Files changed (80) hide show
  1. package/.cli/dist/index.d.ts +1 -0
  2. package/.cli/dist/index.js +12417 -0
  3. package/.cli/host/.react-router/types/app/+types/root.ts +40 -0
  4. package/.cli/host/app/root.tsx +23 -0
  5. package/.cli/host/app/routes.ts +5 -0
  6. package/.cli/host/node_modules/.vite/deps/@mdx-js_mdx.js +81 -0
  7. package/.cli/host/node_modules/.vite/deps/@mdx-js_mdx.js.map +7 -0
  8. package/.cli/host/node_modules/.vite/deps/@mdx-js_rollup.js +4003 -0
  9. package/.cli/host/node_modules/.vite/deps/@mdx-js_rollup.js.map +7 -0
  10. package/.cli/host/node_modules/.vite/deps/@radix-ui_react-icons.js +7531 -0
  11. package/.cli/host/node_modules/.vite/deps/@radix-ui_react-icons.js.map +7 -0
  12. package/.cli/host/node_modules/.vite/deps/@radix-ui_react-tabs.js +917 -0
  13. package/.cli/host/node_modules/.vite/deps/@radix-ui_react-tabs.js.map +7 -0
  14. package/.cli/host/node_modules/.vite/deps/_metadata.json +157 -0
  15. package/.cli/host/node_modules/.vite/deps/chunk-2TUXWMP5.js +46 -0
  16. package/.cli/host/node_modules/.vite/deps/chunk-2TUXWMP5.js.map +7 -0
  17. package/.cli/host/node_modules/.vite/deps/chunk-DNIFH2K7.js +3461 -0
  18. package/.cli/host/node_modules/.vite/deps/chunk-DNIFH2K7.js.map +7 -0
  19. package/.cli/host/node_modules/.vite/deps/chunk-ERHH4CDL.js +23875 -0
  20. package/.cli/host/node_modules/.vite/deps/chunk-ERHH4CDL.js.map +7 -0
  21. package/.cli/host/node_modules/.vite/deps/chunk-ETJV5QNK.js +27 -0
  22. package/.cli/host/node_modules/.vite/deps/chunk-ETJV5QNK.js.map +7 -0
  23. package/.cli/host/node_modules/.vite/deps/chunk-GPEJJ3KZ.js +1906 -0
  24. package/.cli/host/node_modules/.vite/deps/chunk-GPEJJ3KZ.js.map +7 -0
  25. package/.cli/host/node_modules/.vite/deps/chunk-IVP26VTC.js +21 -0
  26. package/.cli/host/node_modules/.vite/deps/chunk-IVP26VTC.js.map +7 -0
  27. package/.cli/host/node_modules/.vite/deps/chunk-MJUUSK53.js +894 -0
  28. package/.cli/host/node_modules/.vite/deps/chunk-MJUUSK53.js.map +7 -0
  29. package/.cli/host/node_modules/.vite/deps/chunk-NHN7DW4J.js +928 -0
  30. package/.cli/host/node_modules/.vite/deps/chunk-NHN7DW4J.js.map +7 -0
  31. package/.cli/host/node_modules/.vite/deps/chunk-OO6QDGMA.js +10609 -0
  32. package/.cli/host/node_modules/.vite/deps/chunk-OO6QDGMA.js.map +7 -0
  33. package/.cli/host/node_modules/.vite/deps/chunk-OUN4SSIE.js +205 -0
  34. package/.cli/host/node_modules/.vite/deps/chunk-OUN4SSIE.js.map +7 -0
  35. package/.cli/host/node_modules/.vite/deps/chunk-TKNG6GUJ.js +894 -0
  36. package/.cli/host/node_modules/.vite/deps/chunk-TKNG6GUJ.js.map +7 -0
  37. package/.cli/host/node_modules/.vite/deps/chunk-TM4FKLNO.js +21628 -0
  38. package/.cli/host/node_modules/.vite/deps/chunk-TM4FKLNO.js.map +7 -0
  39. package/.cli/host/node_modules/.vite/deps/chunk-V2IF7L2E.js +12 -0
  40. package/.cli/host/node_modules/.vite/deps/chunk-V2IF7L2E.js.map +7 -0
  41. package/.cli/host/node_modules/.vite/deps/gray-matter.js +3492 -0
  42. package/.cli/host/node_modules/.vite/deps/gray-matter.js.map +7 -0
  43. package/.cli/host/node_modules/.vite/deps/lucide-react.js +34742 -0
  44. package/.cli/host/node_modules/.vite/deps/lucide-react.js.map +7 -0
  45. package/.cli/host/node_modules/.vite/deps/package.json +3 -0
  46. package/.cli/host/node_modules/.vite/deps/react-dom.js +7 -0
  47. package/.cli/host/node_modules/.vite/deps/react-dom.js.map +7 -0
  48. package/.cli/host/node_modules/.vite/deps/react-dom_client.js +39 -0
  49. package/.cli/host/node_modules/.vite/deps/react-dom_client.js.map +7 -0
  50. package/.cli/host/node_modules/.vite/deps/react-router.js +243 -0
  51. package/.cli/host/node_modules/.vite/deps/react-router.js.map +7 -0
  52. package/.cli/host/node_modules/.vite/deps/react-router_dom.js +212 -0
  53. package/.cli/host/node_modules/.vite/deps/react-router_dom.js.map +7 -0
  54. package/.cli/host/node_modules/.vite/deps/react.js +6 -0
  55. package/.cli/host/node_modules/.vite/deps/react.js.map +7 -0
  56. package/.cli/host/node_modules/.vite/deps/react_jsx-dev-runtime.js +913 -0
  57. package/.cli/host/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
  58. package/.cli/host/node_modules/.vite/deps/react_jsx-runtime.js +7 -0
  59. package/.cli/host/node_modules/.vite/deps/react_jsx-runtime.js.map +7 -0
  60. package/.cli/host/node_modules/.vite/deps/remark-frontmatter.js +428 -0
  61. package/.cli/host/node_modules/.vite/deps/remark-frontmatter.js.map +7 -0
  62. package/.cli/host/node_modules/.vite/deps/remark-gfm.js +3122 -0
  63. package/.cli/host/node_modules/.vite/deps/remark-gfm.js.map +7 -0
  64. package/.cli/host/node_modules/.vite/deps/remark-mdx-frontmatter.js +10482 -0
  65. package/.cli/host/node_modules/.vite/deps/remark-mdx-frontmatter.js.map +7 -0
  66. package/.cli/host/node_modules/.vite/deps/unist-util-visit.js +14 -0
  67. package/.cli/host/node_modules/.vite/deps/unist-util-visit.js.map +7 -0
  68. package/.cli/host/node_modules/.vite/deps/vfile.js +8 -0
  69. package/.cli/host/node_modules/.vite/deps/vfile.js.map +7 -0
  70. package/.cli/host/package.json +43 -0
  71. package/.cli/host/postcss.config.cjs +5 -0
  72. package/.cli/host/react-router.config.ts +8 -0
  73. package/.cli/host/tsconfig.json +22 -0
  74. package/.cli/host/vite.config.ts +9 -0
  75. package/.cli/plugins/xyd-plugin-zero/src/pages/api-reference.tsx +406 -0
  76. package/.cli/plugins/xyd-plugin-zero/src/pages/docs.tsx +166 -0
  77. package/.cli/plugins/xyd-plugin-zero/src/pages/root.tsx +37 -0
  78. package/bin.js +3 -0
  79. package/package.json +15 -13
  80. package/postinstall.js +0 -15
@@ -0,0 +1,14 @@
1
+ import {
2
+ CONTINUE,
3
+ EXIT,
4
+ SKIP,
5
+ visit
6
+ } from "./chunk-OUN4SSIE.js";
7
+ import "./chunk-2TUXWMP5.js";
8
+ export {
9
+ CONTINUE,
10
+ EXIT,
11
+ SKIP,
12
+ visit
13
+ };
14
+ //# sourceMappingURL=unist-util-visit.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,8 @@
1
+ import {
2
+ VFile
3
+ } from "./chunk-TKNG6GUJ.js";
4
+ import "./chunk-2TUXWMP5.js";
5
+ export {
6
+ VFile
7
+ };
8
+ //# sourceMappingURL=vfile.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "private": true,
3
+ "name": "@xyd-js/documan-host",
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {},
7
+ "dependencies": {
8
+ "@xyd-js/atlas": "workspace:*",
9
+ "@xyd-js/core": "workspace:*",
10
+ "@xyd-js/content": "workspace:*",
11
+ "@xyd-js/framework": "workspace:*",
12
+ "@xyd-js/gql": "workspace:*",
13
+ "@xyd-js/openapi": "workspace:*",
14
+ "@xyd-js/uniform": "workspace:*",
15
+ "@xyd-js/theme-gusto": "workspace:*",
16
+ "@react-router/node": "^7.1.1",
17
+ "@react-router/serve": "^7.1.1",
18
+ "codehike": "^1.0.3",
19
+ "express": "^4.21.1",
20
+ "isbot": "^5.1.17",
21
+ "react": "^18.3.1",
22
+ "react-dom": "^18.3.1",
23
+ "react-router": "^7.1.1",
24
+ "remark-frontmatter": "^5.0.0",
25
+ "remark-gfm": "^4.0.0",
26
+ "remark-mdx-frontmatter": "^5.0.0",
27
+ "typescript": "^5.6.3",
28
+ "vfile": "^6.0.3",
29
+ "yaml": "^2.6.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^22.7.5",
33
+ "@types/react-dom": "^18.3.1",
34
+ "@mdx-js/rollup": "^3.1.0",
35
+ "@types/react": "^18.3.9",
36
+ "@vitejs/plugin-react": "^4.3.2",
37
+ "autoprefixer": "^10.4.20",
38
+ "postcss": "^8.4.47",
39
+ "typescript": "^5.6.2",
40
+ "vite": "^5.4.9",
41
+ "vite-tsconfig-paths": "^5.0.1"
42
+ }
43
+ }
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ plugins: {
3
+ autoprefixer: {},
4
+ },
5
+ }
@@ -0,0 +1,8 @@
1
+ import type {Config} from "@react-router/dev/config";
2
+
3
+ export default {
4
+ // return a list of URLs to prerender at build time
5
+ async prerender() {
6
+ return ["/example"];
7
+ },
8
+ } satisfies Config;
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "lib": [
7
+ "ES2020",
8
+ "DOM",
9
+ "DOM.Iterable"
10
+ ],
11
+ "skipLibCheck": true,
12
+ "moduleResolution": "bundler",
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "noEmit": false,
16
+ "strict": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "noFallthroughCasesInSwitch": true,
20
+ "jsx": "react-jsx"
21
+ }
22
+ }
@@ -0,0 +1,9 @@
1
+ import {defineConfig} from 'vite';
2
+
3
+ // DO NOT DELETE
4
+ // This file is needed for host `vite` to run
5
+ export default defineConfig(async () => {
6
+ return {
7
+ plugins: [],
8
+ };
9
+ });
@@ -0,0 +1,406 @@
1
+ import path from "path";
2
+ import {promises as fs} from "fs";
3
+
4
+ import React, {useContext} from "react";
5
+ import {redirect, UNSAFE_DataRouterStateContext} from "react-router";
6
+ import remarkFrontmatter from "remark-frontmatter";
7
+ import remarkMdxFrontmatter from "remark-mdx-frontmatter";
8
+ import remarkGfm from "remark-gfm";
9
+ import {parse} from "codehike";
10
+ import {visit} from "unist-util-visit";
11
+ import {recmaCodeHike, remarkCodeHike} from "codehike/mdx";
12
+ import {compile as mdxCompile} from "@mdx-js/mdx";
13
+
14
+ import {PageFrontMatter} from "@xyd-js/core";
15
+ import {renderoll} from "@xyd-js/foo/renderoll";
16
+ import {
17
+ AtlasLazy
18
+ } from "@xyd-js/atlas";
19
+ import getContentComponents from "@xyd-js/components/content";
20
+ import {mapSettingsToProps} from "@xyd-js/framework/hydration";
21
+ import {Framework} from "@xyd-js/framework/react";
22
+ import type {FwSidebarGroupProps} from "@xyd-js/framework/react";
23
+
24
+ import Theme from "virtual:xyd-theme" // TODO: for some reasons this cannot be hydrated by react-router
25
+ import settings from 'virtual:xyd-settings';
26
+
27
+ import "virtual:xyd-theme/index.css"
28
+ import "virtual:xyd-theme-override/index.css"
29
+
30
+ const contentComponents = getContentComponents()
31
+ const ComponentContent = contentComponents.Content
32
+
33
+ // since unist does not support heading level > 6, we need to normalize them
34
+ function normalizeCustomHeadings() {
35
+ return (tree: any) => {
36
+ visit(tree, 'paragraph', (node, index, parent) => {
37
+ if (!node.children[0].value) {
38
+ return
39
+ }
40
+ const match = node.children[0] && node.children[0].value.match(/^(#+)\s+(.*)/);
41
+ if (match) {
42
+ const level = match[1].length;
43
+ const text = match[2];
44
+ if (level > 6) {
45
+ // Create a new heading node with depth 6
46
+ const headingNode = {
47
+ type: 'heading',
48
+ depth: level,
49
+ children: [{type: 'text', value: text}]
50
+ };
51
+ // Replace the paragraph node with the new heading node
52
+ //@ts-ignore
53
+ parent.children[index] = headingNode;
54
+ }
55
+ }
56
+ });
57
+ };
58
+ }
59
+
60
+ const codeHikeOptions = {
61
+ lineNumbers: true,
62
+ showCopyButton: true,
63
+ autoImport: true,
64
+ components: {},
65
+ // syntaxHighlighting: { // TODO: !!! FROM SETTINGS !!! wait for rr7 rsc ??
66
+ // theme: "github-dark",
67
+ // },
68
+ };
69
+
70
+ // TODO: map every file and merge them or load via client-side ?
71
+ async function compileBySlug(slug: string) {
72
+ // TODO: cwd ?
73
+ let filePath = path.join(process.cwd(), `${slug}.md`)
74
+
75
+ try {
76
+ await fs.access(filePath)
77
+ } catch (_) {
78
+ filePath = path.join(process.cwd(), `${slug}.mdx`)
79
+
80
+ await fs.access(filePath)
81
+ }
82
+
83
+ const content = await fs.readFile(filePath, "utf-8");
84
+
85
+ return await compile(content)
86
+ }
87
+
88
+ async function compile(content: string): Promise<string> {
89
+ const compiled = await mdxCompile(content, {
90
+ remarkPlugins: [
91
+ normalizeCustomHeadings,
92
+ [remarkCodeHike, codeHikeOptions],
93
+ remarkFrontmatter,
94
+ remarkMdxFrontmatter,
95
+ remarkGfm
96
+ ],
97
+ rehypePlugins: [],
98
+ recmaPlugins: [
99
+ [recmaCodeHike, codeHikeOptions]
100
+ ],
101
+ outputFormat: 'function-body',
102
+ development: false,
103
+ });
104
+
105
+ return String(compiled)
106
+ }
107
+
108
+ interface loaderData {
109
+ sidebarGroups: FwSidebarGroupProps[]
110
+ toc: PageFrontMatter
111
+ slug: string
112
+ code: string
113
+ }
114
+
115
+ function getPathname(url: string) {
116
+ const parsedUrl = new URL(url);
117
+ return parsedUrl.pathname.replace(/^\//, '');
118
+ }
119
+
120
+ // TODO: fix any
121
+ function findFirstUrl(items: any): string {
122
+ const queue = [...items];
123
+
124
+ while (queue.length > 0) {
125
+ const item = queue.shift();
126
+
127
+ if (item.href) {
128
+ return item.href;
129
+ }
130
+
131
+ if (item.items) {
132
+ queue.push(...item.items);
133
+ }
134
+ }
135
+
136
+ return "";
137
+ }
138
+
139
+ // TODO: fix any
140
+ export async function loader({request}: { request: any }) {
141
+ const slug = getPathname(request.url)
142
+
143
+ let code = ""
144
+ let error: any
145
+
146
+ try {
147
+ code = await compileBySlug(slug)
148
+ } catch (e) {
149
+ error = e
150
+ }
151
+
152
+ const {groups: sidebarGroups} = await mapSettingsToProps(
153
+ settings,
154
+ slug
155
+ )
156
+
157
+ // TODO: dry with docs.tsx - resolver?
158
+ if (error) {
159
+ if (sidebarGroups && error.code === "ENOENT") {
160
+ const firstItem = findFirstUrl(sidebarGroups?.[0]?.items)
161
+
162
+ if (firstItem) {
163
+ return redirect(firstItem)
164
+ }
165
+ }
166
+
167
+ console.error(error)
168
+ }
169
+
170
+ return {
171
+ sidebarGroups,
172
+ slug,
173
+ code
174
+ } as loaderData
175
+ }
176
+
177
+ function mdxExport(code: string) {
178
+ const scope = {
179
+ Fragment: React.Fragment,
180
+ jsxs: React.createElement,
181
+ jsx: React.createElement,
182
+ jsxDEV: React.createElement,
183
+ }
184
+ const fn = new Function(...Object.keys(scope), code)
185
+ return fn(scope)
186
+ }
187
+
188
+ function renderollAsyncClient(routeId: string, slug: string) {
189
+ return async () => {
190
+ let mod;
191
+ let urlPrefix;
192
+ let data;
193
+
194
+ // TODO: fix any
195
+ function moduleData(mods: any, id: string) {
196
+ mod = mods.default[id]
197
+ if (!mod) {
198
+ throw new Error(`Unknown openapi id: ${id}`)
199
+ }
200
+ urlPrefix = mod.urlPrefix
201
+ data = mod.data
202
+ }
203
+
204
+ switch (routeId) {
205
+ case "xyd-plugin-zero/graphql": {
206
+ // @ts-ignore
207
+ mod = await import("virtual:xyd-plugin-zero/graphql");
208
+ urlPrefix = "/docs/api/graphql" // TODO: dynamic urlPreifx
209
+ data = mod.default.data
210
+ break;
211
+ }
212
+ case "xyd-plugin-zero/openapi": {
213
+ // @ts-ignore
214
+ mod = await import("virtual:xyd-plugin-zero/openapi");
215
+ urlPrefix = "/docs/api/openapi" // TODO: dynamic urlPrefix
216
+ data = mod.default.data
217
+ break;
218
+ }
219
+ default: {
220
+ if (routeId.includes("xyd-plugin-zero/openapi")) {
221
+ const [_, id] = routeId.split("xyd-plugin-zero/openapi-")
222
+ // @ts-ignore
223
+ const mods = await import("virtual:xyd-plugin-zero/openapi")
224
+ moduleData(mods, id)
225
+ break;
226
+ }
227
+ if (routeId.includes("xyd-plugin-zero/graphql")) {
228
+ const [_, id] = routeId.split("xyd-plugin-zero/graphql-")
229
+ // @ts-ignore
230
+ const mods = await import("virtual:xyd-plugin-zero/graphql")
231
+ moduleData(mods, id)
232
+ break;
233
+ }
234
+
235
+ throw new Error(`Unknown routeId: ${routeId}`);
236
+ }
237
+ }
238
+
239
+ if (!Array.isArray(data)) {
240
+ console.warn(`mod.default is not an array, current type is: ${typeof mod.default}`)
241
+
242
+ return
243
+ }
244
+
245
+ // TODO: in the future custom position
246
+ const prevRefs = []
247
+ const nextRefs = []
248
+ const mdxComponentsPrev: any[] = []
249
+ const mdxComponentsNext: any[] = []
250
+
251
+ let pos = 0;
252
+
253
+ for (const chunk of data) {
254
+ if (!chunk) {
255
+ continue
256
+ }
257
+
258
+ if (chunk.slug === slug) {
259
+ pos = 1
260
+ continue
261
+ }
262
+
263
+ const references = pos === 0 ? prevRefs : nextRefs
264
+
265
+ const code = await compile(chunk.content) // TODO: do we need real path?
266
+ const mdx = mdxExport(code)
267
+ const Content = mdx.default
268
+ const content = Content ? parse(Content, {
269
+ components: contentComponents
270
+ }) : null
271
+
272
+ // TODO: support non-fererence pages
273
+ if (content.references) {
274
+ references.push(...(content?.references || []) as [])
275
+ } else {
276
+ const mdxComponents = pos === 0 ? mdxComponentsPrev : mdxComponentsNext
277
+
278
+ mdxComponents.push(<div data-slug={`/${chunk.slug}`}>
279
+ <ComponentContent>
280
+ {content}
281
+ </ComponentContent>
282
+ </div>)
283
+ }
284
+ }
285
+
286
+ return [
287
+ ({onLoaded}) => <>
288
+ <ComponentContent>
289
+ {mdxComponentsPrev}
290
+ </ComponentContent>
291
+
292
+ {
293
+ prevRefs.length ? <div>
294
+ <AtlasLazy
295
+ references={prevRefs}
296
+ urlPrefix={urlPrefix}
297
+ slug={slug}
298
+ onLoaded={onLoaded}
299
+ />
300
+ </div> : null
301
+ }
302
+ </>,
303
+
304
+ ({onLoaded}) => <>
305
+ <ComponentContent>
306
+ {mdxComponentsNext}
307
+ </ComponentContent>
308
+
309
+ {
310
+ nextRefs.length ? <div>
311
+ <AtlasLazy
312
+ references={nextRefs}
313
+ urlPrefix={urlPrefix}
314
+ slug={slug}
315
+ onLoaded={onLoaded}
316
+ />
317
+ </div> : null
318
+ }
319
+ </>
320
+ ]
321
+ }
322
+ }
323
+
324
+ function getRouteId() {
325
+ const routerState = useContext(UNSAFE_DataRouterStateContext)
326
+ let routeId: string = ""
327
+
328
+ routerState?.matches?.forEach(match => {
329
+ const loader = routerState?.loaderData[match?.route?.id]
330
+
331
+ if (loader) {
332
+ routeId = match?.route?.id
333
+ }
334
+ })
335
+
336
+ return routeId
337
+ }
338
+
339
+ function MemoMDXComponent(codeComponent: any) {
340
+ return React.useMemo(
341
+ () => codeComponent ? codeComponent : null,
342
+ [codeComponent]
343
+ )
344
+ }
345
+
346
+ // // TODO: move to content?
347
+ function mdxContent(code: string) {
348
+ const content = mdxExport(code) // TODO: fix any
349
+ if (!mdxExport) {
350
+ return {}
351
+ }
352
+
353
+ return {
354
+ component: content?.default,
355
+ }
356
+ }
357
+
358
+ // TODO: in the future more smoother loader - first fast server render then move to ideal position of client and then replace and 3 items at start
359
+ export default function APIReference({loaderData}: { loaderData: loaderData }) {
360
+ const content = mdxContent(loaderData.code)
361
+ const serverComponent = content ? parse(content.component, {
362
+ components: contentComponents
363
+ }) : null
364
+
365
+ const memoizedServerComponent = MemoMDXComponent(serverComponent)
366
+
367
+ const serverAtlasOrMDX = memoizedServerComponent?.references ?
368
+ <AtlasLazy
369
+ references={memoizedServerComponent?.references || []}
370
+ slug={loaderData.slug.startsWith("/") ? loaderData.slug : `/${loaderData.slug}`}
371
+ urlPrefix="/"
372
+ /> :
373
+ <ComponentContent>
374
+ {memoizedServerComponent}
375
+ </ComponentContent>
376
+
377
+ const routeId = getRouteId()
378
+
379
+ const RenderollContent = renderoll(
380
+ renderollAsyncClient(routeId, loaderData.slug),
381
+ {
382
+ decorator: ({children}) => <ComponentContent>
383
+ {children}
384
+ </ComponentContent>
385
+ }
386
+ )
387
+
388
+ return <Framework
389
+ settings={settings}
390
+ sidebarGroups={loaderData.sidebarGroups}
391
+ >
392
+ <Theme
393
+ themeSettings={{
394
+ hideToc: true,
395
+ bigArticle: true,
396
+ sidebar: {
397
+ clientSideRouting: true
398
+ }
399
+ }}
400
+ >
401
+ <RenderollContent>
402
+ {serverAtlasOrMDX}
403
+ </RenderollContent>
404
+ </Theme>
405
+ </Framework>
406
+ }
@@ -0,0 +1,166 @@
1
+ import path from "node:path";
2
+
3
+ import React from "react";
4
+ import {redirect} from "react-router";
5
+
6
+ import {PageFrontMatter} from "@xyd-js/core"
7
+ import {compileBySlug} from "@xyd-js/content"
8
+ import getContentComponents from "@xyd-js/components/content";
9
+ import {HomePage} from "@xyd-js/components/pages";
10
+ import type {IBreadcrumb, INavLinks} from "@xyd-js/ui";
11
+ import {mapSettingsToProps} from "@xyd-js/framework/hydration";
12
+ import {Framework, FwNav} from "@xyd-js/framework/react";
13
+ import type {FwSidebarGroupProps} from "@xyd-js/framework/react";
14
+
15
+ import settings from 'virtual:xyd-settings';
16
+ import Theme from "virtual:xyd-theme";
17
+
18
+ import "virtual:xyd-theme/index.css"
19
+ import "virtual:xyd-theme-override/index.css"
20
+
21
+ interface loaderData {
22
+ sidebarGroups: FwSidebarGroupProps[]
23
+ breadcrumbs: IBreadcrumb[],
24
+ navlinks?: INavLinks,
25
+ toc: PageFrontMatter
26
+ slug: string
27
+ code: string
28
+ }
29
+
30
+ const contentComponents = getContentComponents()
31
+
32
+ const supportedExtensions = {
33
+ ".mdx": true,
34
+ ".md": true,
35
+ "": true,
36
+ }
37
+
38
+ function getPathname(url: string) {
39
+ const parsedUrl = new URL(url);
40
+ return parsedUrl.pathname.replace(/^\//, '');
41
+ }
42
+
43
+ export async function loader({request}: { request: any }) {
44
+ const slug = getPathname(request.url || "index") || "index"
45
+ const ext = path.extname(slug)
46
+
47
+ if (!supportedExtensions[ext]) {
48
+ return {}
49
+ }
50
+
51
+ let code = ""
52
+ let error: any
53
+
54
+ try {
55
+ code = await compileBySlug(slug, true)
56
+ } catch (e) {
57
+ error = e
58
+ }
59
+
60
+ if (error?.code === "ENOENT") {
61
+ try {
62
+ // TODO: better index algorithm
63
+ code = await compileBySlug(slug + "/index", true)
64
+ } catch (e) {
65
+ error = e
66
+ }
67
+ }
68
+
69
+ const {
70
+ groups: sidebarGroups,
71
+ breadcrumbs,
72
+ navlinks,
73
+ } = await mapSettingsToProps(
74
+ settings,
75
+ slug
76
+ )
77
+
78
+ if (error) {
79
+ if (sidebarGroups && error.code === "ENOENT") {
80
+ const firstItem = sidebarGroups?.[0]?.items?.[0]
81
+
82
+ if (firstItem) {
83
+ return redirect(firstItem.href)
84
+ }
85
+ }
86
+
87
+ console.error(error)
88
+ }
89
+
90
+ return {
91
+ sidebarGroups,
92
+ breadcrumbs,
93
+ navlinks,
94
+ slug,
95
+ code,
96
+ } as loaderData
97
+ }
98
+
99
+ // TODO: move to content?
100
+ function mdxExport(code: string) {
101
+ const scope = {
102
+ Fragment: React.Fragment,
103
+ jsxs: React.createElement,
104
+ jsx: React.createElement,
105
+ jsxDEV: React.createElement,
106
+ }
107
+ const fn = new Function(...Object.keys(scope), code)
108
+ return fn(scope)
109
+ }
110
+
111
+ // // TODO: move to content?
112
+ function mdxContent(code: string) {
113
+ const content = mdxExport(code) // TODO: fix any
114
+ if (!mdxExport) {
115
+ return {}
116
+ }
117
+
118
+ return {
119
+ component: content?.default,
120
+ toc: content?.toc,
121
+ frontmatter: content?.frontmatter,
122
+ themeSettings: content?.themeSettings || {},
123
+ page: content?.page || false,
124
+ }
125
+ }
126
+
127
+ export function MemoMDXComponent(codeComponent: any) {
128
+ return React.useMemo(
129
+ () => codeComponent ? codeComponent : null,
130
+ [codeComponent]
131
+ )
132
+ }
133
+
134
+ export default function CustomPage({loaderData, ...rest}: { loaderData: loaderData }) {
135
+ const content = mdxContent(loaderData.code)
136
+ const Component = MemoMDXComponent(content.component)
137
+
138
+ return <Framework
139
+ settings={settings}
140
+ sidebarGroups={loaderData.sidebarGroups || []}
141
+ toc={content.toc || []}
142
+ breadcrumbs={loaderData.breadcrumbs || []}
143
+ navlinks={loaderData.navlinks}
144
+ >
145
+ {content?.page ? <Component components={{
146
+ ...contentComponents,
147
+ // TODO: another page components
148
+ HomePage: (props) => <HomePage
149
+ {...props}
150
+ // TODO: get props from theme about nav (middle etc)
151
+ // TODO: footer
152
+ // TODO: style
153
+ header={<div style={{marginLeft: "var(--xyd-global-page-gutter)"}}>
154
+ <FwNav kind="middle"/>
155
+ </div>}
156
+
157
+ >
158
+ {props.children}
159
+ </HomePage>,
160
+ }}/> : <Theme
161
+ themeSettings={content.themeSettings}
162
+ >
163
+ {Component ? <Component components={contentComponents}/> : <></>}
164
+ </Theme>}
165
+ </Framework>
166
+ }