@xyd-js/uniform 0.1.0-build.172

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,132 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import fs from 'fs';
3
+ import { pluginJsonView } from '../pluginJsonView';
4
+ import type { Reference } from '../../types';
5
+ import uniform from '../../index'
6
+
7
+ describe('pluginJsonView', () => {
8
+ it('should handle properties without examples', () => {
9
+ const plugin = pluginJsonView();
10
+
11
+ const inputs: Reference[] = [
12
+ {
13
+ title: 'Test title',
14
+ description: 'Test description',
15
+ canonical: 'test-canonical',
16
+ examples: {
17
+ groups: []
18
+ },
19
+ "definitions": [
20
+ {
21
+ "title": "Properties",
22
+ "properties": [
23
+ {
24
+ "name": "charset",
25
+ "type": "string",
26
+ "description": "Character encoding for the document\n",
27
+ "examples": [
28
+ "\"UTF-8\""
29
+ ]
30
+ },
31
+ {
32
+ "name": "robots",
33
+ "type": "string",
34
+ "description": "Standard meta tag for controlling search engine crawling and indexing\n",
35
+ "examples": [
36
+ "\"index, follow\"",
37
+ "\"noindex, nofollow\""
38
+ ]
39
+ }
40
+ ]
41
+ }
42
+ ]
43
+ },
44
+ {
45
+ title: 'Test title',
46
+ description: 'Test description',
47
+ canonical: 'test-canonical',
48
+ examples: {
49
+ groups: []
50
+ },
51
+ "definitions": [
52
+ {
53
+ "title": "Properties",
54
+ "properties": [
55
+ {
56
+ "name": "charset",
57
+ "type": "string",
58
+ "description": "Character encoding for the document\n",
59
+ "examples": [
60
+ "UTF-8"
61
+ ]
62
+ },
63
+ {
64
+ "name": "robots",
65
+ "type": "string",
66
+ "description": "Standard meta tag for controlling search engine crawling and indexing\n",
67
+ "examples": [
68
+ "index, follow",
69
+ "noindex, nofollow"
70
+ ]
71
+ }
72
+ ]
73
+ }
74
+ ]
75
+ },
76
+ {
77
+ title: 'Test title',
78
+ description: 'Test description',
79
+ canonical: 'test-canonical',
80
+ examples: {
81
+ groups: []
82
+ },
83
+ "definitions": [
84
+ {
85
+ "title": "Properties",
86
+ "properties": [
87
+ {
88
+ "name": "robots",
89
+ "type": "string",
90
+ "description": "Standard meta tag for controlling search engine crawling and indexing\n",
91
+ "examples": [
92
+ "index, follow",
93
+ "noindex, nofollow"
94
+ ]
95
+ },
96
+ {
97
+ "name": "charset",
98
+ "type": "string",
99
+ "description": "Character encoding for the document\n",
100
+ "examples": [
101
+ "UTF-8"
102
+ ]
103
+ },
104
+ ]
105
+ }
106
+ ]
107
+ }
108
+ ];
109
+
110
+ const outputs: string[] = [
111
+ '{\n' +
112
+ ' "charset": "UTF-8",\n' +
113
+ ' "robots": "index, follow" // or "noindex, nofollow"\n' +
114
+ '}',
115
+
116
+ '{\n' +
117
+ ' "charset": "UTF-8",\n' +
118
+ ' "robots": "index, follow" // or "noindex, nofollow"\n' +
119
+ '}',
120
+
121
+ '{\n' +
122
+ ' "robots": "index, follow", // or "noindex, nofollow"\n' +
123
+ ' "charset": "UTF-8"\n' +
124
+ '}',
125
+ ]
126
+ const result = uniform(inputs, {
127
+ plugins: [plugin]
128
+ });
129
+
130
+ expect(result.out.jsonViews).toStrictEqual(outputs);
131
+ });
132
+ });
@@ -0,0 +1,2 @@
1
+ export { pluginJsonView } from "./pluginJsonView"
2
+ export { pluginNavigation } from "./pluginNavigation"
@@ -0,0 +1,54 @@
1
+ import type { UniformPluginArgs, UniformPlugin } from "../index";
2
+ import { Reference } from "../types";
3
+
4
+ export interface pluginJsonViewOptions {
5
+ }
6
+
7
+ type pluginJsonViewOutput = {
8
+ jsonViews: string;
9
+ }
10
+
11
+ export function pluginJsonView(
12
+ options?: pluginJsonViewOptions
13
+ ): UniformPlugin<pluginJsonViewOutput> {
14
+
15
+ return function pluginJsonViewInner({
16
+ defer,
17
+ }: UniformPluginArgs) {
18
+ const jsonViews: string[] = [];
19
+
20
+ defer(() => ({
21
+ jsonViews
22
+ }))
23
+
24
+ return (ref: Reference) => {
25
+ // Build the output string manually to ensure exact format
26
+ const lines: string[] = [];
27
+ lines.push('{');
28
+
29
+ ref.definitions.forEach(def => {
30
+ def.properties.forEach((prop, index) => {
31
+ // Remove any quotes and trailing characters from the value
32
+ const value = (prop.examples?.[0] || '').replace(/^"|"$|[^a-zA-Z0-9\s\-_.,:/@#=;+()]/g, '');
33
+ const comment = prop.examples && prop.examples.length > 1
34
+ ? ` // or "${(prop.examples as string[])[1].replace(/^"|"$|[^a-zA-Z0-9\s\-_.,:/@#=;+()]/g, '')}"`
35
+ : '';
36
+ const isLast = index === def.properties.length - 1;
37
+ // Add comma after the value but before the comment
38
+ lines.push(` "${prop.name}": "${value}"${isLast ? '' : ','}${comment}`);
39
+ });
40
+ });
41
+
42
+ lines.push('}');
43
+
44
+ jsonViews.push(lines.join('\n'));
45
+ }
46
+ }
47
+ }
48
+
49
+ // example usage:
50
+ // const response = uniform([/* references */], {
51
+ // plugins: [pluginJsonView({
52
+ //
53
+ // })],
54
+ // });
@@ -0,0 +1,135 @@
1
+ import path from 'node:path';
2
+
3
+ import type { Sidebar, Metadata, MetadataMap, Settings, PageURL } from "@xyd-js/core";
4
+
5
+ import type { UniformPluginArgs, UniformPlugin } from "../index";
6
+ import { CodeBlockTab, Example, ExampleGroup, Reference } from "../types";
7
+
8
+ const DEFAULT_VIRTUAL_FOLDER = ".xyd/.cache/.content" // TODO: share this + .xyd/.build/.content for build
9
+
10
+ const DEFAULT_GROUP_NAME = "API Reference" // TODO: configurable
11
+
12
+ type GroupMap = {
13
+ [key: string]: {
14
+ __groups: GroupMap
15
+ pages: Set<string>
16
+ }
17
+ }
18
+
19
+ export interface pluginNavigationOptions {
20
+ urlPrefix: string
21
+ defaultGroup?: string
22
+ }
23
+
24
+ type pluginNavigationOutput = {
25
+ pageFrontMatter: MetadataMap;
26
+ sidebar: Sidebar[];
27
+ }
28
+
29
+ export function pluginNavigation(
30
+ settings: Settings,
31
+ options: pluginNavigationOptions
32
+ ): UniformPlugin<pluginNavigationOutput> {
33
+ if (!options.urlPrefix) {
34
+ throw new Error("urlPrefix is required")
35
+ }
36
+
37
+ return function pluginNavigationInner({
38
+ defer,
39
+ }: UniformPluginArgs) {
40
+ const pageFrontMatter: MetadataMap = {}
41
+ const groupMaps: GroupMap = {}
42
+
43
+ defer(() => ({
44
+ pageFrontMatter,
45
+ sidebar: convertGroupMapsToSidebar(settings, groupMaps) as Sidebar[]
46
+ }))
47
+
48
+ return (ref: Reference) => {
49
+ const dataCtx = ref.context
50
+ const pagePath = path.join(options.urlPrefix, ref.canonical)
51
+
52
+ let group = dataCtx?.group || []
53
+ let title = ref.title
54
+
55
+ if (pageFrontMatter[pagePath]) {
56
+ console.error("(pluginNavigation): pageFrontMatter[pagePath] already exists", pagePath)
57
+ }
58
+
59
+ if (!group) {
60
+ group = [options.defaultGroup || DEFAULT_GROUP_NAME]
61
+ }
62
+
63
+ pageFrontMatter[pagePath] = {
64
+ title,
65
+ }
66
+
67
+ if (typeof group === "string") {
68
+ // TODO: seek nested group (it's not always from 0)
69
+ throw new Error("group as string is not supported yet")
70
+ }
71
+
72
+ group.reduce((groups: GroupMap, groupName: string, i: number) => {
73
+ if (!groups[groupName]) {
74
+ groups[groupName] = {
75
+ __groups: {},
76
+ pages: new Set()
77
+ }
78
+ }
79
+
80
+ if (i === group.length - 1) {
81
+ groups[groupName].pages.add(pagePath)
82
+ }
83
+
84
+ return groups[groupName].__groups
85
+ }, groupMaps)
86
+ }
87
+ }
88
+ }
89
+
90
+ function convertGroupMapsToSidebar(settings: Settings, groupMaps: GroupMap): Sidebar[] {
91
+ const nav: Sidebar[] = []
92
+
93
+ Object.keys(groupMaps).map((groupName) => {
94
+ const current = groupMaps[groupName]
95
+
96
+ const pages: PageURL[] = []
97
+
98
+ current.pages.forEach((page: string) => {
99
+ if (settings?.engine?.uniform?.store) {
100
+ pages.push(page)
101
+ return
102
+ }
103
+ pages.push({
104
+ virtual: path.join(DEFAULT_VIRTUAL_FOLDER, page),
105
+ page: page,
106
+ })
107
+ })
108
+
109
+ if (Object.keys(current.__groups).length) {
110
+ const subNav: Sidebar = {
111
+ group: groupName,
112
+ pages: convertGroupMapsToSidebar(settings, current.__groups)
113
+ }
114
+
115
+ nav.push(subNav)
116
+
117
+ return
118
+ }
119
+
120
+ nav.push({
121
+ group: groupName,
122
+ pages,
123
+ })
124
+ })
125
+
126
+ return nav
127
+ }
128
+
129
+ // TODO: in the future xyd settings must be removed cuz uniform will be part of opendocs
130
+ // example usage:
131
+ // const response = uniform([/* references */], {
132
+ // plugins: [pluginNavigation({}, {
133
+ // urlPrefix: "/docs"
134
+ // })],
135
+ // });
package/src/types.ts ADDED
@@ -0,0 +1,294 @@
1
+ import React from "react";
2
+ import {HighlightedCode} from "codehike/code";
3
+
4
+ // TODO: type, and category also as generic?
5
+ export interface Reference<
6
+ C = ReferenceContext,
7
+ M extends DefinitionMeta = DefinitionMeta,
8
+ VM extends DefinitionVariantMeta = DefinitionVariantMeta
9
+ > {
10
+ title: string;
11
+ description: string | React.ReactNode;
12
+ canonical: string;
13
+
14
+ definitions: Definition<M, VM>[] // TODO: in the future from generic?
15
+ examples: ExampleRoot
16
+
17
+
18
+ category?: ReferenceCategory; // TODO: do we need that?
19
+
20
+ type?: ReferenceType; // TODO: do we need that?
21
+
22
+ context?: C;
23
+
24
+ /**
25
+ * TODO: !!!! BETTER !!!!
26
+ * @internal
27
+ */
28
+ __UNSAFE_selector?: (selector: string) => any;
29
+ }
30
+
31
+ export type DefinitionOpenAPIMeta = Meta<"contentType" | "required" | "definitionDescription">;
32
+ export type DefinitionTypeDocMeta = Meta<"type">;
33
+ export type DefinitionGraphqlMeta = Meta<"type" | "graphqlName">;
34
+
35
+ export type DefinitionMeta = DefinitionOpenAPIMeta | DefinitionTypeDocMeta | DefinitionGraphqlMeta
36
+
37
+ export type SymbolDef = {
38
+ id?: string | string[];
39
+
40
+ canonical?: string | string[];
41
+ }
42
+
43
+ export interface Definition<
44
+ M extends DefinitionMeta = DefinitionMeta,
45
+ VM extends DefinitionVariantMeta = DefinitionVariantMeta
46
+ > {
47
+ title: string;
48
+
49
+ properties: DefinitionProperty[];
50
+
51
+ rootProperty?: DefinitionProperty
52
+
53
+ variants?: DefinitionVariant<VM>[];
54
+
55
+ description?: string | React.ReactNode;
56
+
57
+ meta?: M[];
58
+
59
+ /**
60
+ * @internal
61
+ */
62
+ symbolDef?: SymbolDef;
63
+
64
+ /**
65
+ * @internal
66
+ */
67
+ id?: string;
68
+
69
+ /**
70
+ * @internal
71
+ */
72
+ type?: string;
73
+ }
74
+
75
+ export type DefinitionVariantOpenAPIMeta = Meta<"status" | "contentType" | "definitionDescription" | "required">;
76
+ export type CommonDefinitionVariantMeta = Meta<"symbolName">;
77
+
78
+ export type DefinitionVariantMeta = CommonDefinitionVariantMeta | DefinitionVariantOpenAPIMeta
79
+
80
+ export interface DefinitionVariant<
81
+ M extends DefinitionVariantMeta = DefinitionVariantMeta
82
+ > {
83
+ title: string;
84
+
85
+ properties: DefinitionProperty[];
86
+
87
+ rootProperty?: DefinitionProperty
88
+
89
+ description?: string | React.ReactNode;
90
+
91
+ symbolDef?: SymbolDef;
92
+
93
+ meta?: M[];
94
+ }
95
+
96
+ export interface Meta<T = string> {
97
+ name: T;
98
+
99
+ value?: unknown; // TODO: better type?
100
+ }
101
+
102
+ export type DefinitionPropertyMeta = Meta<"required" | "deprecated" | "internal" | "defaults" | "nullable" | "example" | "examples" | "minimum" | "maximum" | "enum-type"> // TODO: better solution than enum-type?
103
+
104
+ export enum DEFINED_DEFINITION_PROPERTY_TYPE {
105
+ UNION = "$$union",
106
+
107
+ XOR = "$$xor",
108
+
109
+ ARRAY = "$$array",
110
+
111
+ ENUM = "$$enum",
112
+
113
+ // TYPE = "$$type", TODO: good idea?
114
+ }
115
+
116
+ export interface DefinitionProperty {
117
+ name: string;
118
+
119
+ type: string | DEFINED_DEFINITION_PROPERTY_TYPE
120
+
121
+ description: string | React.ReactNode;
122
+
123
+ // TODO: in the future more advanced examples?
124
+ examples?: string | string[];
125
+
126
+ symbolDef?: SymbolDef;
127
+
128
+ meta?: DefinitionPropertyMeta[];
129
+
130
+ context?: any // TODO: better type
131
+
132
+ properties?: DefinitionProperty[];
133
+
134
+ ofProperty?: DefinitionProperty;
135
+ }
136
+
137
+ export interface ExampleRoot {
138
+ groups: ExampleGroup[];
139
+ }
140
+
141
+ export interface ExampleGroup {
142
+ description?: string;
143
+
144
+ examples: Example[];
145
+ }
146
+
147
+ export interface Example {
148
+ description?: string; // TODO: replace with title ?
149
+
150
+ codeblock: CodeBlock;
151
+ }
152
+
153
+ export interface CodeBlock {
154
+ title?: string;
155
+
156
+ tabs: CodeBlockTab[];
157
+ }
158
+
159
+ export interface CodeBlockTab {
160
+ // title of the tab e.g "JavaScript"
161
+ title: string;
162
+
163
+ // code in the tab e.g "console.log('Hello World')"
164
+ code: string
165
+
166
+ // language of the code e.g "js"
167
+ language: string;
168
+
169
+ // context of the generation method e.g openapi or graphql
170
+ context?: ExampleContext;
171
+
172
+ // TODO: highlighted code
173
+ highlighted?: HighlightedCode;
174
+ }
175
+
176
+ export type ExampleContext = GraphQLExampleContext | OpenAPIExampleContext;
177
+
178
+ // TODO: concept only
179
+ export enum ReferenceCategory {
180
+ // for React
181
+ COMPONENTS = "components",
182
+ HOOKS = "hooks",
183
+ // end for React
184
+
185
+ // for API
186
+ REST = "rest",
187
+ GRAPHQL = "graphql",
188
+ // end for API
189
+
190
+ // for code
191
+ FUNCTIONS = "functions",
192
+ //
193
+ }
194
+
195
+ // TODO: concept only
196
+ export enum ReferenceType {
197
+ // for React
198
+ COMPONENT = "component",
199
+ HOOK = "hook",
200
+ // end for React
201
+
202
+ // for API
203
+ // TODO: better type system for specific api typesl like gql or rest
204
+ REST_HTTP_GET = "rest_get",
205
+ REST_HTTP_POST = "rest_post",
206
+ REST_HTTP_PUT = "rest_put",
207
+ REST_HTTP_PATCH = "rest_patch",
208
+ REST_HTTP_DELETE = "rest_delete",
209
+ REST_HTTP_OPTIONS = "rest_options",
210
+ REST_HTTP_HEAD = "rest_head",
211
+ REST_HTTP_TRACE = "rest_trace",
212
+
213
+ REST_COMPONENT_SCHEMA = "rest_component_schema",
214
+ // ---
215
+ GRAPHQL_QUERY = "graphql_query",
216
+ GRAPHQL_MUTATION = "graphql_mutation",
217
+ GRAPHQL_SUBSCRIPTION = "graphql_subscription",
218
+
219
+ GRAPHQL_SCALAR = "graphql_scalar",
220
+ GRAPHQL_OBJECT = "graphql_object",
221
+ GRAPHQL_INTERFACE = "graphql_interface",
222
+ GRAPHQL_UNION = "graphql_union",
223
+ GRAPHQL_ENUM = "graphql_enum",
224
+ GRAPHQL_INPUT = "graphql_input",
225
+ // end for API
226
+
227
+ // for code
228
+ FUNCTION_JS = "function_js",
229
+ // end for code
230
+ }
231
+
232
+ export interface BaseReferenceContext {
233
+ group?: string[];
234
+
235
+ scopes?: string[];
236
+ }
237
+
238
+ export interface GraphQLReferenceContext extends BaseReferenceContext {
239
+ /**
240
+ * @internal
241
+ */
242
+ graphqlTypeShort: string;
243
+
244
+ graphqlName: string;
245
+ }
246
+
247
+ // TODO: custom value?
248
+ export interface OpenAPIReferenceContext extends BaseReferenceContext {
249
+ method?: string;
250
+
251
+ path?: string;
252
+
253
+ fullPath?: string;
254
+
255
+ componentSchema?: string
256
+ }
257
+
258
+ export type TypeDocReferenceContextMeta = Meta<"internal">
259
+
260
+ // Add TypeDocReferenceContext to the union type
261
+ export interface TypeDocReferenceContext extends BaseReferenceContext {
262
+ symbolId: string;
263
+ symbolName: string;
264
+ symbolKind: number;
265
+ packageName: string;
266
+ fileName: string;
267
+ fileFullPath: string;
268
+ line: number;
269
+ col: number;
270
+ signatureText: {
271
+ code: string;
272
+ lang: string;
273
+ };
274
+ sourcecode: {
275
+ code: string;
276
+ lang: string;
277
+ };
278
+ category?: string;
279
+ meta?: TypeDocReferenceContextMeta[]
280
+ }
281
+
282
+ export type ReferenceContext = GraphQLReferenceContext | OpenAPIReferenceContext | TypeDocReferenceContext
283
+
284
+ export interface GraphQLExampleContext {
285
+ schema?: any; // TODO:
286
+ }
287
+
288
+ export interface OpenAPIExampleContext {
289
+ status?: number;
290
+
291
+ content?: string;
292
+ }
293
+
294
+
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "paths": {
4
+ "@xyd-js/core": ["../xyd-core/index.ts"],
5
+ },
6
+ "target": "ES2020",
7
+ "module": "ESNext",
8
+ "moduleResolution": "node",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "outDir": "./dist",
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "incremental": true,
17
+ "tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo",
18
+ },
19
+ "include": ["examples/basic-example/**/*.ts", "src/**/*.ts"],
20
+ "exclude": ["node_modules", "dist"]
21
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,39 @@
1
+ import {defineConfig, Options} from 'tsup';
2
+
3
+ const config: Options = {
4
+ entry: {
5
+ index: 'index.ts',
6
+ markdown: 'markdown.ts',
7
+ content: 'content.ts',
8
+ },
9
+ format: ['esm', 'cjs'], // Output both ESM and CJS formats
10
+ target: 'node16', // Ensure compatibility with Node.js 16
11
+ dts: {
12
+ entry: {
13
+ index: 'index.ts',
14
+ markdown: 'markdown.ts',
15
+ content: 'content.ts',
16
+ },
17
+ resolve: true, // Resolve external types
18
+ },
19
+ splitting: false, // Disable code splitting
20
+ sourcemap: true, // Generate source maps
21
+ clean: true, // Clean the output directory before each build
22
+ esbuildOptions: (options) => {
23
+ options.platform = 'node'; // Ensure the platform is set to Node.js
24
+ options.external = [
25
+ 'react',
26
+ 'fs',
27
+ 'path',
28
+ 'node:fs',
29
+ 'node:fs/promises',
30
+
31
+ 'codehike',
32
+ 'unist-util-visit',
33
+ '@mdx-js/mdx'
34
+ ]; // Mark these modules as external
35
+ options.loader = { '.js': 'jsx' }; // Ensure proper handling of .js files
36
+ },
37
+ }
38
+
39
+ export default defineConfig(config);
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ include: ['src/**/*.test.ts' ,'packages/ts/**/*.test.ts'],
8
+ coverage: {
9
+ provider: 'v8',
10
+ reporter: ['text', 'json', 'html'],
11
+ include: ['src/**/*.ts', 'packages/ts/**/*.ts'],
12
+ exclude: ['src/**/*.test.ts', 'src/**/*.d.ts', 'packages/ts/**/*.test.ts', 'packages/ts/**/*.d.ts']
13
+ }
14
+ }
15
+ });