@xyd-js/uniform 0.0.0-build

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/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@xyd-js/uniform",
3
+ "version": "0.0.0-build+6952c2c-20250813013245",
4
+ "description": "",
5
+ "main": "./dist/index.js",
6
+ "type": "module",
7
+ "exports": {
8
+ "./package.json": "./package.json",
9
+ ".": "./dist/index.js",
10
+ "./markdown": "./dist/markdown.js",
11
+ "./content": "./dist/content.js"
12
+ },
13
+ "dependencies": {
14
+ "codehike": "^1.0.3",
15
+ "mdast-util-from-markdown": "^2.0.2",
16
+ "remark": "^15.0.1",
17
+ "remark-stringify": "^11.0.0",
18
+ "unist-builder": "^4.0.0",
19
+ "unist-util-visit": "^5.0.0",
20
+ "@xyd-js/core": "0.0.0-build+6952c2c-20250813013245"
21
+ },
22
+ "peerDependencies": {
23
+ "@mdx-js/mdx": "^3.1.0"
24
+ },
25
+ "devDependencies": {
26
+ "@vitest/coverage-v8": "^1.3.1",
27
+ "@types/node": "^22.10.5",
28
+ "rimraf": "^3.0.2",
29
+ "tsup": "^8.3.0",
30
+ "typescript": "^4.5.5",
31
+ "vitest": "^1.3.1"
32
+ },
33
+ "scripts": {
34
+ "clean": "rimraf build",
35
+ "prebuild": "pnpm clean",
36
+ "build": "tsup",
37
+ "test": "vitest",
38
+ "test:coverage": "vitest run --coverage"
39
+ }
40
+ }
@@ -0,0 +1,124 @@
1
+ import path from "path";
2
+ import {promises as fs} from "fs";
3
+
4
+ import React from "react";
5
+ import {parse} from "codehike";
6
+ import {visit} from "unist-util-visit";
7
+ import {recmaCodeHike, remarkCodeHike} from "codehike/mdx";
8
+ import {compile as mdxCompile} from "@mdx-js/mdx";
9
+
10
+ const codeHikeOptions = {
11
+ lineNumbers: true,
12
+ showCopyButton: true,
13
+ autoImport: true,
14
+ components: {code: "Code"},
15
+ // syntaxHighlighting: { // TODO: !!! FROM SETTINGS !!! wait for rr7 rsc ??
16
+ // theme: "github-dark",
17
+ // },
18
+ };
19
+ //
20
+ // // since unist does not support heading level > 6, we need to normalize them
21
+ function normalizeCustomHeadings() {
22
+ return (tree: any) => {
23
+ visit(tree, 'paragraph', (node, index, parent) => {
24
+ const match = node.children[0] && node.children[0].value.match(/^(#+)\s+(.*)/);
25
+ if (match) {
26
+ const level = match[1].length;
27
+ const text = match[2];
28
+ if (level > 6) {
29
+ // Create a new heading node with depth 6
30
+ const headingNode = {
31
+ type: 'heading',
32
+ depth: level,
33
+ children: [{type: 'text', value: text}]
34
+ };
35
+ // Replace the paragraph node with the new heading node
36
+ //@ts-ignore
37
+ parent.children[index] = headingNode;
38
+ }
39
+ }
40
+ });
41
+ };
42
+ }
43
+
44
+ //
45
+ async function compile(content: string): Promise<string> {
46
+ const compiled = await mdxCompile(content, {
47
+ remarkPlugins: [normalizeCustomHeadings, [remarkCodeHike, codeHikeOptions]],
48
+ recmaPlugins: [
49
+ [recmaCodeHike, codeHikeOptions]
50
+ ],
51
+ outputFormat: 'function-body',
52
+ development: false,
53
+ });
54
+
55
+ return String(compiled)
56
+ }
57
+
58
+ async function compileBySlug(slug: string) {
59
+ // TODO: cwd ?
60
+ const filePath = path.join(process.cwd(), `${slug}.md`)
61
+
62
+ try {
63
+ await fs.access(filePath)
64
+ } catch (e) {
65
+ console.error(e)
66
+ return ""
67
+ }
68
+
69
+ const content = await fs.readFile(filePath, "utf-8");
70
+
71
+ return await compile(content)
72
+ }
73
+
74
+ function getMDXComponent(code: string) {
75
+ const mdxExport = getMDXExport(code)
76
+ return mdxExport.default
77
+ }
78
+
79
+ function getMDXExport(code: string) {
80
+ const scope = {
81
+ Fragment: React.Fragment,
82
+ jsxs: React.createElement,
83
+ jsx: React.createElement,
84
+ jsxDEV: React.createElement,
85
+ }
86
+ const fn = new Function(...Object.keys(scope), code)
87
+ return fn(scope)
88
+ }
89
+
90
+ //
91
+ // function MDXContent(code: string) {
92
+ // return React.useMemo(
93
+ // () => code ? getMDXComponent(code) : null,
94
+ // [code]
95
+ // )
96
+ // }
97
+
98
+ // TODO: mod ts
99
+ async function lazyReferences(mod: any) {
100
+ const references: any[] = []
101
+
102
+ if (Array.isArray(mod.default)) {
103
+ for (const chunk of mod.default) {
104
+ try {
105
+ const code = await compile(chunk) // TODO: do we need real path?
106
+ const Content = getMDXComponent(code)
107
+ const content = Content ? parse(Content) : null
108
+
109
+ references.push(...content.references as [])
110
+ } catch (e) {
111
+ console.error(e)
112
+ }
113
+ }
114
+ } else {
115
+ console.warn(`mod.default is not an array, current type is: ${typeof mod.default}`)
116
+ }
117
+
118
+ return references
119
+ }
120
+
121
+ export {
122
+ compileBySlug,
123
+ lazyReferences
124
+ }
package/src/index.ts ADDED
@@ -0,0 +1,105 @@
1
+ // Define the new PluginV type with a callback function that returns another function
2
+ import {Reference} from "./types";
3
+
4
+ export * from "./types";
5
+
6
+ // Define the new PluginV type with a callback function that returns another function
7
+ export type UniformPluginArgs = {
8
+ references: Reference[] | Reference,
9
+ defer: (defer: () => any) => void;
10
+
11
+ // TODO: maybe in the future
12
+ // visit: (selector: string | "[method] [path]", callback: (...args: any[]) => void) => void;
13
+ }
14
+
15
+
16
+ export type UniformPluginRestArgs = {
17
+ index: number;
18
+ }
19
+ export type UniformPlugin<T> = (args: UniformPluginArgs) => (ref: Reference, restArgs: UniformPluginRestArgs) => void;
20
+
21
+ // Utility type to infer if a type is an array and avoid wrapping it into an array twice
22
+ type NormalizeArray<T> = T extends Array<infer U> ? U[] : T;
23
+
24
+ // Infer the return type of the plugin callback properly and normalize array types
25
+ type PluginResult<T extends UniformPlugin<any>> = T extends UniformPlugin<infer R> ? R : never;
26
+
27
+ // Merge all plugin return types into a single object and normalize arrays
28
+ type MergePluginResults<T extends UniformPlugin<any>[]> = {
29
+ [K in keyof UnionToIntersection<PluginResult<T[number]>>]: NormalizeArray<UnionToIntersection<PluginResult<T[number]>>[K]>
30
+ };
31
+
32
+ // Utility type to handle intersection to an object type
33
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
34
+
35
+ // Implement the uniform function
36
+ export default function uniform<T extends UniformPlugin<any>[]>(
37
+ references: Reference[],
38
+ config: { plugins: T }
39
+ ) {
40
+ // Infer the merged result type from all plugins
41
+ type ResultType = MergePluginResults<T>;
42
+
43
+ // Initialize the response with a type-safe out object
44
+ const response: {
45
+ references: Reference[]
46
+ out: { [K in keyof ResultType]: ResultType[K] }
47
+ } = {
48
+ references,
49
+ out: {} as { [K in keyof ResultType]: ResultType[K] }
50
+ };
51
+
52
+ config.plugins.forEach((plugin) => {
53
+ let defer: any = undefined; // fix any
54
+
55
+ const call = plugin({
56
+ references: references,
57
+ defer: (cb) => {
58
+ if (typeof cb === "function") {
59
+ defer = cb
60
+ }
61
+ },
62
+ // visit: (pattern, callback) => {
63
+ // }
64
+ })
65
+
66
+ references.map((ref, i) => {
67
+ call(ref, {
68
+ index: i,
69
+ })
70
+ });
71
+
72
+ if (typeof defer === "function") {
73
+ const resp = defer()
74
+ if (typeof resp !== "object") {
75
+ throw new Error(`Invalid callback return type: ${typeof resp}`)
76
+ }
77
+
78
+ response.out = {
79
+ ...response.out,
80
+ ...resp
81
+ }
82
+ }
83
+ })
84
+
85
+ return response;
86
+ }
87
+
88
+ // Example usage
89
+ // const examplePlugin: UniformPlugin<{ value: boolean }> = (cb) => {
90
+ // return (ref: Reference) => {
91
+ // };
92
+ // };
93
+ // function examplePlugin(defer: (defer: () => { value: boolean }) => void) {
94
+ // defer(() => ({
95
+ // value: true,
96
+ // }));
97
+ //
98
+ // return (ref: Reference) => {
99
+ //
100
+ // };
101
+ // }
102
+ // const response = uniform([/* references */], {
103
+ // plugins: [examplePlugin],
104
+ // });
105
+ // response.out
@@ -0,0 +1,67 @@
1
+ import {remark} from "remark";
2
+ import remarkStringify from "remark-stringify";
3
+
4
+ import {Reference} from "../types";
5
+
6
+ import {definitions, examples, heading, root} from "./utils";
7
+
8
+ // TODO: any
9
+ export function compile(ast: any) {
10
+ return remark()
11
+ // .use(unlimitedHeadings)
12
+ .use(remarkStringify, {
13
+ bullet: '-',
14
+ fences: true,
15
+ listItemIndent: 'one',
16
+ incrementListMarker: false,
17
+ handlers: {
18
+ heading(node) {
19
+ return `${"#".repeat(node.depth)} ${node.children[0].value}`;
20
+ },
21
+ },
22
+ })
23
+ .stringify(root(ast));
24
+ }
25
+
26
+ export function referenceAST(ref: Reference) {
27
+ const md = []
28
+
29
+ const mdHeading = heading(
30
+ ref.title,
31
+ ref.canonical,
32
+ typeof ref.description === "string" ? ref.description : "",
33
+ ref.category,
34
+ ref.type,
35
+ ref.context
36
+ )
37
+
38
+ md.push(
39
+ mdHeading.title,
40
+ )
41
+
42
+ if (mdHeading?.description?.length) {
43
+ md.push(...mdHeading.description)
44
+ }
45
+
46
+ md.push(
47
+ mdHeading.canonical,
48
+ )
49
+
50
+ if (mdHeading.category) {
51
+ md.push(mdHeading.category)
52
+ }
53
+
54
+ if (mdHeading.type) {
55
+ md.push(mdHeading.type)
56
+ }
57
+
58
+ if (mdHeading?.context?.length) {
59
+ md.push(...mdHeading.context)
60
+ }
61
+
62
+ const mdExamples = examples(ref.examples)
63
+ const mdDefinitions = definitions(ref.definitions)
64
+ md.push(...mdExamples, ...mdDefinitions)
65
+
66
+ return md;
67
+ }
@@ -0,0 +1,270 @@
1
+ import { fromMarkdown } from "mdast-util-from-markdown";
2
+ import {u} from "unist-builder";
3
+
4
+ import {
5
+ ExampleRoot,
6
+ Definition,
7
+ ReferenceCategory,
8
+ ReferenceContext,
9
+ ReferenceType,
10
+ DefinitionProperty
11
+ } from "../types";
12
+
13
+ // START_DEPTH_LEVEL is the start depth level for the markdown AST
14
+ // starts from 2 because 1 is reserved for the title
15
+ const START_DEPTH_LEVEL = 2
16
+
17
+ // TODO: fix any
18
+ export function root(ast: any) {
19
+ return u('root', ast);
20
+ }
21
+
22
+ export function heading(
23
+ title: string,
24
+ canonical: string,
25
+ description: string,
26
+ refCategory?: ReferenceCategory,
27
+ refType?: ReferenceType,
28
+ refContext?: ReferenceContext
29
+ ) {
30
+ const uTitle = u(
31
+ 'heading',
32
+ {depth: START_DEPTH_LEVEL},
33
+ [u('text', `!!references ${title}`)]
34
+ )
35
+
36
+ const uCanonical = u(
37
+ 'heading',
38
+ {depth: uTitle.depth + 1},
39
+ [u('text', `!canonical ${canonical}`)]
40
+ )
41
+
42
+ let uDesc = [
43
+ u(
44
+ 'heading',
45
+ {depth: uTitle.depth + 1},
46
+ [u('text', `!description`),]
47
+ ),
48
+ u('paragraph', [u('text', description)])
49
+ ]
50
+
51
+ let uRefCategory
52
+ if (refCategory) {
53
+ uRefCategory = u(
54
+ 'heading',
55
+ {depth: uTitle.depth + 1},
56
+ [u('text', `!category ${refCategory}`)]
57
+ )
58
+ }
59
+
60
+ let uRefType
61
+ if (refType) {
62
+ uRefType = u(
63
+ 'heading',
64
+ {depth: uTitle.depth + 1},
65
+ [u('text', `!type ${refType}`)]
66
+ )
67
+ }
68
+
69
+ let uContext = []
70
+
71
+ if (refContext && Object.keys(refContext)) {
72
+ uContext.push(u(
73
+ 'heading',
74
+ {depth: uTitle.depth + 1},
75
+ [
76
+ u('text', `!context`),
77
+ ]
78
+ ))
79
+
80
+
81
+ for (const [key, value] of Object.entries(refContext)) {
82
+ if (typeof value === "object") {
83
+ // TODO: support ```<lang> ??
84
+ if (value.code) {
85
+ uContext.push(
86
+ u('heading', {depth: uContext[0].depth + 1}, [u('text', `!${key}`)])
87
+ );
88
+
89
+ uContext.push(
90
+ u('code', {lang: value.lang}, value.code)
91
+ );
92
+
93
+ continue;
94
+ }
95
+ }
96
+
97
+ uContext.push(
98
+ u('heading', {depth: uContext[0].depth + 1}, [u('text', `!${key} ${value.toString()}`)])
99
+ );
100
+ }
101
+ }
102
+
103
+ return {
104
+ title: uTitle,
105
+ canonical: uCanonical,
106
+ description: uDesc,
107
+ category: uRefCategory || null,
108
+ type: uRefType || null,
109
+ context: uContext || null
110
+ }
111
+ }
112
+
113
+ export function examples(examples: ExampleRoot) {
114
+ const md = []
115
+
116
+ const uExampleRoot = u(
117
+ 'heading',
118
+ {depth: START_DEPTH_LEVEL + 1},
119
+ [u('text', `!examples`)]
120
+ )
121
+ md.push(uExampleRoot)
122
+
123
+ examples.groups.forEach(group => {
124
+ const uExampleGroups = u(
125
+ 'heading',
126
+ {depth: uExampleRoot.depth + 1},
127
+ [u('text', `!!groups`)]
128
+ )
129
+ md.push(uExampleGroups)
130
+
131
+ const uGroupDescription = u(
132
+ 'heading',
133
+ {depth: uExampleGroups.depth + 1},
134
+ [u('text', `!description ${group.description}`)]
135
+ )
136
+ md.push(uGroupDescription)
137
+
138
+ group.examples.forEach(example => {
139
+ const uExamples = u(
140
+ 'heading',
141
+ {depth: uExampleGroups.depth + 1},
142
+ [u('text', `!!examples`)]
143
+ )
144
+ md.push(uExamples)
145
+
146
+ const codeBlock = u(
147
+ 'heading',
148
+ {depth: uExamples.depth + 1},
149
+ [u('text', `!codeblock`)]
150
+ )
151
+ md.push(codeBlock)
152
+
153
+ const codeBlockTitle = u(
154
+ 'heading',
155
+ {depth: codeBlock.depth + 1},
156
+ [u('text', `!title ${example.codeblock.title}`)]
157
+ )
158
+ md.push(codeBlockTitle)
159
+
160
+ const tabs = u(
161
+ 'heading',
162
+ {depth: codeBlock.depth + 1},
163
+ [u('text', `!!tabs`)]
164
+ )
165
+ md.push(tabs)
166
+
167
+ example.codeblock.tabs.forEach(tab => {
168
+ const code = u('code', {
169
+ lang: tab.language,
170
+ meta: `!code ${tab.title}`
171
+ }, tab.code);
172
+
173
+ md.push(code)
174
+ })
175
+
176
+ })
177
+ })
178
+
179
+ return md
180
+ }
181
+
182
+ export function definitions(definitions: Definition[]) {
183
+ const md: any[] = []
184
+
185
+ definitions.forEach(definition => {
186
+ const uDefinition = u(
187
+ 'heading',
188
+ {depth: START_DEPTH_LEVEL + 1},
189
+ [u('text', `!!definitions`)]
190
+ )
191
+ md.push(uDefinition)
192
+
193
+ md.push(u(
194
+ 'heading',
195
+ {depth: uDefinition.depth + 1},
196
+ [u('text', `!title ${definition.title}`)]
197
+ ))
198
+
199
+ definition.properties.forEach(prop => {
200
+ properties(
201
+ uDefinition.depth + 1,
202
+ {
203
+ name: prop.name,
204
+ type: prop.type,
205
+ description: prop.description,
206
+ context: prop.context,
207
+ properties: prop.properties // TODO: fix ts
208
+ },
209
+ md,
210
+ )
211
+ })
212
+ })
213
+
214
+ return md
215
+ }
216
+
217
+ // TODO: any[]
218
+ export function properties(
219
+ depth: number,
220
+ props: DefinitionProperty,
221
+ output: any[] = []
222
+ ) {
223
+ const paramName = props.name;
224
+
225
+ const propTitle = `!!properties ${paramName}`; // TODO: check if `!!properties is enough`
226
+ const uPropTitle = u('heading', {depth}, [u('text', propTitle)]);
227
+ const uPropName = u('paragraph', {depth}, [u('text', `!name ${paramName}`)]);
228
+ const uPropType = u('paragraph', {depth}, [u('text', `!type ${props.type}`)]);
229
+ const markdownAst = fromMarkdown(props.description || '');
230
+ const uPropDesc = u("paragraph", { depth }, markdownAst.children);
231
+ const uContext = []
232
+
233
+ if (props.context && Object.keys(props.context)) {
234
+ uContext.push(u(
235
+ 'heading',
236
+ {depth: depth + 1},
237
+ [
238
+ u('text', `!context`),
239
+ ]
240
+ ))
241
+
242
+ for (const [key, value] of Object.entries(props.context)) {
243
+ uContext.push(u(
244
+ 'heading',
245
+ {depth: uContext[0].depth + 1},
246
+ [u('text', `!${key} ${value}`)]
247
+ )
248
+ )
249
+ }
250
+ }
251
+
252
+ output.push(
253
+ uPropTitle,
254
+ uPropName,
255
+ uPropType,
256
+ uPropDesc,
257
+ ...uContext
258
+ );
259
+
260
+ if (props.properties) {
261
+ const deepDepth = depth + 1
262
+
263
+ for (const prop of props.properties) {
264
+ properties(deepDepth, prop, output)
265
+ }
266
+ }
267
+ }
268
+
269
+
270
+