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