@xyd-js/cli 0.1.0-xyd.28 → 0.1.0-xyd.29
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.
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import {promises as fs} from "fs";
|
|
3
|
+
|
|
4
|
+
import React, {} from "react";
|
|
5
|
+
import {redirect} 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 getContentComponents from "@xyd-js/components/content";
|
|
16
|
+
import {mapSettingsToProps} from "@xyd-js/framework/hydration";
|
|
17
|
+
import {Framework, type FwSidebarGroupProps} from "@xyd-js/framework/react";
|
|
18
|
+
import {AtlasIndex} from "@xyd-js/atlas/atlas-index";
|
|
19
|
+
import type {IBreadcrumb, INavLinks} from "@xyd-js/ui";
|
|
20
|
+
|
|
21
|
+
import Theme from "virtual:xyd-theme" // TODO: for some reasons this cannot be hydrated by react-router
|
|
22
|
+
import settings from 'virtual:xyd-settings';
|
|
23
|
+
|
|
24
|
+
import "virtual:xyd-theme/index.css"
|
|
25
|
+
import "virtual:xyd-theme-override/index.css"
|
|
26
|
+
|
|
27
|
+
interface loaderData {
|
|
28
|
+
sidebarGroups: FwSidebarGroupProps[]
|
|
29
|
+
breadcrumbs: IBreadcrumb[],
|
|
30
|
+
navlinks?: INavLinks,
|
|
31
|
+
toc: PageFrontMatter
|
|
32
|
+
slug: string
|
|
33
|
+
code: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const contentComponents = getContentComponents()
|
|
37
|
+
|
|
38
|
+
// since unist does not support heading level > 6, we need to normalize them
|
|
39
|
+
function normalizeCustomHeadings() {
|
|
40
|
+
return (tree: any) => {
|
|
41
|
+
visit(tree, 'paragraph', (node, index, parent) => {
|
|
42
|
+
if (!node.children[0].value) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
const match = node.children[0] && node.children[0].value.match(/^(#+)\s+(.*)/);
|
|
46
|
+
if (match) {
|
|
47
|
+
const level = match[1].length;
|
|
48
|
+
const text = match[2];
|
|
49
|
+
if (level > 6) {
|
|
50
|
+
// Create a new heading node with depth 6
|
|
51
|
+
const headingNode = {
|
|
52
|
+
type: 'heading',
|
|
53
|
+
depth: level,
|
|
54
|
+
children: [{type: 'text', value: text}]
|
|
55
|
+
};
|
|
56
|
+
// Replace the paragraph node with the new heading node
|
|
57
|
+
//@ts-ignore
|
|
58
|
+
parent.children[index] = headingNode;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const codeHikeOptions = {
|
|
66
|
+
lineNumbers: true,
|
|
67
|
+
showCopyButton: true,
|
|
68
|
+
autoImport: true,
|
|
69
|
+
components: {},
|
|
70
|
+
// syntaxHighlighting: { // TODO: !!! FROM SETTINGS !!! wait for rr7 rsc ??
|
|
71
|
+
// theme: "github-dark",
|
|
72
|
+
// },
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const compiledBySlug = {}
|
|
76
|
+
|
|
77
|
+
// TODO: map every file and merge them or load via client-side ?
|
|
78
|
+
async function compileBySlug(slug: string) {
|
|
79
|
+
if (compiledBySlug[slug]) {
|
|
80
|
+
return compiledBySlug[slug]
|
|
81
|
+
}
|
|
82
|
+
console.time("api-reference compileBySlug")
|
|
83
|
+
// TODO: cwd ?
|
|
84
|
+
let filePath = path.join(process.cwd(), `${slug}.md`)
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
await fs.access(filePath)
|
|
88
|
+
} catch (_) {
|
|
89
|
+
filePath = path.join(process.cwd(), `${slug}.mdx`)
|
|
90
|
+
|
|
91
|
+
await fs.access(filePath)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.time("api-reference readFile")
|
|
95
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
96
|
+
console.timeEnd("api-reference readFile")
|
|
97
|
+
|
|
98
|
+
console.time("api-reference compile")
|
|
99
|
+
const resp = await compile(content)
|
|
100
|
+
console.timeEnd("api-reference compile")
|
|
101
|
+
|
|
102
|
+
console.timeEnd("api-reference compileBySlug")
|
|
103
|
+
compiledBySlug[slug] = resp
|
|
104
|
+
return resp
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function compile(content: string): Promise<string> {
|
|
108
|
+
const compiled = await mdxCompile(content, {
|
|
109
|
+
remarkPlugins: [
|
|
110
|
+
normalizeCustomHeadings,
|
|
111
|
+
[
|
|
112
|
+
remarkCodeHike,
|
|
113
|
+
codeHikeOptions
|
|
114
|
+
],
|
|
115
|
+
remarkFrontmatter,
|
|
116
|
+
remarkMdxFrontmatter,
|
|
117
|
+
remarkGfm
|
|
118
|
+
],
|
|
119
|
+
rehypePlugins: [],
|
|
120
|
+
recmaPlugins: [
|
|
121
|
+
[
|
|
122
|
+
recmaCodeHike,
|
|
123
|
+
codeHikeOptions
|
|
124
|
+
]
|
|
125
|
+
],
|
|
126
|
+
outputFormat: 'function-body',
|
|
127
|
+
development: false,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return String(compiled)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
interface loaderData {
|
|
134
|
+
sidebarGroups: FwSidebarGroupProps[]
|
|
135
|
+
toc: PageFrontMatter
|
|
136
|
+
slug: string
|
|
137
|
+
code: string
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getPathname(url: string) {
|
|
141
|
+
const parsedUrl = new URL(url);
|
|
142
|
+
return parsedUrl.pathname.replace(/^\//, '');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// TODO: fix any
|
|
146
|
+
function findFirstUrl(items: any): string {
|
|
147
|
+
const queue = [...items];
|
|
148
|
+
|
|
149
|
+
while (queue.length > 0) {
|
|
150
|
+
const item = queue.shift();
|
|
151
|
+
|
|
152
|
+
if (item.href) {
|
|
153
|
+
return item.href;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (item.items) {
|
|
157
|
+
queue.push(...item.items);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return "";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
interface data {
|
|
165
|
+
groups: FwSidebarGroupProps[],
|
|
166
|
+
breadcrumbs: IBreadcrumb[]
|
|
167
|
+
navlinks?: INavLinks
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const mapSettingsToPropsMap: { [key: string]: data } = {}
|
|
171
|
+
|
|
172
|
+
// TODO: fix any
|
|
173
|
+
export async function loader({request}: { request: any }) {
|
|
174
|
+
console.time("api-reference loader")
|
|
175
|
+
const slug = getPathname(request.url);
|
|
176
|
+
|
|
177
|
+
let code = "";
|
|
178
|
+
let error: any;
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
code = await compileBySlug(slug);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
error = e;
|
|
184
|
+
}
|
|
185
|
+
let data: data
|
|
186
|
+
|
|
187
|
+
if (!mapSettingsToPropsMap[slug]) {
|
|
188
|
+
data = await mapSettingsToProps(
|
|
189
|
+
settings,
|
|
190
|
+
slug
|
|
191
|
+
);
|
|
192
|
+
mapSettingsToPropsMap[slug] = data
|
|
193
|
+
} else {
|
|
194
|
+
data = mapSettingsToPropsMap[slug]
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const {groups: sidebarGroups, breadcrumbs, navlinks} = data;
|
|
198
|
+
|
|
199
|
+
if (error) {
|
|
200
|
+
if (sidebarGroups && error.code === "ENOENT") {
|
|
201
|
+
const firstItem = findFirstUrl(sidebarGroups?.[0]?.items);
|
|
202
|
+
|
|
203
|
+
if (firstItem) {
|
|
204
|
+
return redirect(firstItem);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
console.error(error);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.timeEnd("api-reference loader")
|
|
212
|
+
return {
|
|
213
|
+
sidebarGroups,
|
|
214
|
+
breadcrumbs,
|
|
215
|
+
navlinks,
|
|
216
|
+
slug,
|
|
217
|
+
code,
|
|
218
|
+
} as loaderData;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function mdxExport(code: string) {
|
|
222
|
+
const scope = {
|
|
223
|
+
Fragment: React.Fragment,
|
|
224
|
+
jsxs: React.createElement,
|
|
225
|
+
jsx: React.createElement,
|
|
226
|
+
jsxDEV: React.createElement,
|
|
227
|
+
}
|
|
228
|
+
const fn = new Function(...Object.keys(scope), code)
|
|
229
|
+
return fn(scope)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function MemoMDXComponent(codeComponent: any) {
|
|
233
|
+
return React.useMemo(
|
|
234
|
+
() => codeComponent ? codeComponent : null,
|
|
235
|
+
[codeComponent]
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// // TODO: move to content?
|
|
240
|
+
function mdxContent(code: string) {
|
|
241
|
+
const content = mdxExport(code) // TODO: fix any
|
|
242
|
+
if (!mdxExport) {
|
|
243
|
+
return {}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
component: content?.default,
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// TODO: below is a concept only
|
|
252
|
+
// class MyThemeSettings extends BaseThemeSettings {
|
|
253
|
+
// private constructor() {
|
|
254
|
+
// super();
|
|
255
|
+
//
|
|
256
|
+
// this.toc.hide(true)
|
|
257
|
+
// this.sidebar.clientSideRouting(true)
|
|
258
|
+
// this.layout.size("large")
|
|
259
|
+
// }
|
|
260
|
+
//
|
|
261
|
+
// override abc() {
|
|
262
|
+
//
|
|
263
|
+
// }
|
|
264
|
+
//
|
|
265
|
+
// static new() {
|
|
266
|
+
// return new MyThemeSettings()
|
|
267
|
+
// }
|
|
268
|
+
// }
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
// TODO: below is a concept only
|
|
272
|
+
// const themeSettings = new ThemeSettings()
|
|
273
|
+
// .toc.hide()
|
|
274
|
+
// .sidebar({
|
|
275
|
+
// clientSideRouting: true
|
|
276
|
+
// })
|
|
277
|
+
// .layout.size("large")
|
|
278
|
+
|
|
279
|
+
// 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
|
|
280
|
+
export default function APIReference({loaderData}: { loaderData: loaderData }) {
|
|
281
|
+
const content = mdxContent(loaderData.code)
|
|
282
|
+
const serverComponent = content ? parse(content.component, {
|
|
283
|
+
components: contentComponents
|
|
284
|
+
}) : null
|
|
285
|
+
|
|
286
|
+
const memoizedServerComponent = MemoMDXComponent(serverComponent)
|
|
287
|
+
|
|
288
|
+
console.log("memoizedServerComponent", memoizedServerComponent)
|
|
289
|
+
|
|
290
|
+
return <Framework
|
|
291
|
+
settings={settings}
|
|
292
|
+
sidebarGroups={loaderData.sidebarGroups || []}
|
|
293
|
+
breadcrumbs={loaderData.breadcrumbs || []}
|
|
294
|
+
navlinks={loaderData.navlinks}
|
|
295
|
+
>
|
|
296
|
+
<Theme
|
|
297
|
+
themeSettings={{
|
|
298
|
+
hideToc: true,
|
|
299
|
+
sidebar: {
|
|
300
|
+
clientSideRouting: true
|
|
301
|
+
},
|
|
302
|
+
layout: {
|
|
303
|
+
size: "large"
|
|
304
|
+
}
|
|
305
|
+
}}
|
|
306
|
+
>
|
|
307
|
+
<AtlasIndex
|
|
308
|
+
data={memoizedServerComponent?.references[0]}
|
|
309
|
+
/>
|
|
310
|
+
</Theme>
|
|
311
|
+
</Framework>
|
|
312
|
+
}
|
|
@@ -26,7 +26,22 @@ interface loaderData {
|
|
|
26
26
|
code: string
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const contentComponents =
|
|
29
|
+
const contentComponents = {
|
|
30
|
+
...getContentComponents(),
|
|
31
|
+
|
|
32
|
+
HomePage: (props) => <HomePage
|
|
33
|
+
{...props}
|
|
34
|
+
// TODO: get props from theme about nav (middle etc)
|
|
35
|
+
// TODO: footer
|
|
36
|
+
// TODO: style
|
|
37
|
+
header={<div style={{marginLeft: "var(--xyd-global-page-gutter)"}}>
|
|
38
|
+
<FwNav kind="middle"/>
|
|
39
|
+
</div>}
|
|
40
|
+
|
|
41
|
+
>
|
|
42
|
+
{props.children}
|
|
43
|
+
</HomePage>
|
|
44
|
+
}
|
|
30
45
|
|
|
31
46
|
const supportedExtensions = {
|
|
32
47
|
".mdx": true,
|
|
@@ -142,7 +157,32 @@ export function MemoMDXComponent(codeComponent: any) {
|
|
|
142
157
|
|
|
143
158
|
export default function CustomPage({loaderData, ...rest}: { loaderData: loaderData }) {
|
|
144
159
|
const content = mdxContent(loaderData.code)
|
|
145
|
-
const
|
|
160
|
+
const Content = MemoMDXComponent(content.component)
|
|
161
|
+
|
|
162
|
+
if (!Content) {
|
|
163
|
+
console.error("Content not found")
|
|
164
|
+
return null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let component: React.JSX.Element
|
|
168
|
+
|
|
169
|
+
if (content.page) {
|
|
170
|
+
component = <Content
|
|
171
|
+
components={{
|
|
172
|
+
...contentComponents,
|
|
173
|
+
}}
|
|
174
|
+
/>
|
|
175
|
+
} else {
|
|
176
|
+
component = <Theme
|
|
177
|
+
themeSettings={content.themeSettings}
|
|
178
|
+
>
|
|
179
|
+
<Content
|
|
180
|
+
components={{
|
|
181
|
+
...contentComponents,
|
|
182
|
+
}}
|
|
183
|
+
/>
|
|
184
|
+
</Theme>
|
|
185
|
+
}
|
|
146
186
|
|
|
147
187
|
return <Framework
|
|
148
188
|
settings={settings}
|
|
@@ -151,25 +191,6 @@ export default function CustomPage({loaderData, ...rest}: { loaderData: loaderDa
|
|
|
151
191
|
breadcrumbs={loaderData.breadcrumbs || []}
|
|
152
192
|
navlinks={loaderData.navlinks}
|
|
153
193
|
>
|
|
154
|
-
{
|
|
155
|
-
...contentComponents,
|
|
156
|
-
// TODO: another page components
|
|
157
|
-
HomePage: (props) => <HomePage
|
|
158
|
-
{...props}
|
|
159
|
-
// TODO: get props from theme about nav (middle etc)
|
|
160
|
-
// TODO: footer
|
|
161
|
-
// TODO: style
|
|
162
|
-
header={<div style={{marginLeft: "var(--xyd-global-page-gutter)"}}>
|
|
163
|
-
<FwNav kind="middle"/>
|
|
164
|
-
</div>}
|
|
165
|
-
|
|
166
|
-
>
|
|
167
|
-
{props.children}
|
|
168
|
-
</HomePage>,
|
|
169
|
-
}}/> : <Theme
|
|
170
|
-
themeSettings={content.themeSettings}
|
|
171
|
-
>
|
|
172
|
-
{Component ? <Component components={contentComponents}/> : <></>}
|
|
173
|
-
</Theme>}
|
|
194
|
+
{component}
|
|
174
195
|
</Framework>
|
|
175
196
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xyd-js/cli",
|
|
3
|
-
"version": "0.1.0-xyd.
|
|
3
|
+
"version": "0.1.0-xyd.29",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"author": "",
|
|
6
6
|
"description": "",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"colors": "^1.4.0",
|
|
63
63
|
"semver": "^7.6.3",
|
|
64
64
|
"vite-tsconfig-paths": "^5.1.4",
|
|
65
|
-
"@xyd-js/documan": "0.1.0-xyd.
|
|
65
|
+
"@xyd-js/documan": "0.1.0-xyd.24"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "tsup",
|