@hubspot/ui-extensions-sdk-api-metadata 0.11.6 → 0.12.1
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/README.md +43 -16
- package/dist/__generated__/component-props.js +1574 -175
- package/dist/__tests__/component-props-docs.spec.d.ts +1 -0
- package/dist/__tests__/component-props-docs.spec.js +295 -0
- package/dist/__tests__/jsx-utils.spec.d.ts +1 -0
- package/dist/__tests__/jsx-utils.spec.js +162 -0
- package/dist/__tests__/markdown-utils.spec.d.ts +1 -0
- package/dist/__tests__/markdown-utils.spec.js +13 -0
- package/dist/component-props-docs.d.ts +8 -0
- package/dist/component-props-docs.js +111 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/internal/get-type-source.d.ts +15 -0
- package/dist/internal/get-type-source.js +399 -0
- package/dist/internal/render-prop-type.d.ts +31 -0
- package/dist/internal/render-prop-type.js +325 -0
- package/dist/internal/utils/jsdoc-utils.d.ts +29 -0
- package/dist/internal/utils/jsdoc-utils.js +52 -0
- package/dist/internal/utils/jsx-utils.d.ts +32 -0
- package/dist/internal/utils/jsx-utils.js +77 -0
- package/dist/internal/utils/markdown-utils.d.ts +12 -0
- package/dist/internal/utils/markdown-utils.js +21 -0
- package/dist/types.d.ts +132 -0
- package/dist/types.js +102 -0
- package/package.json +12 -8
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { isArrayNode, isBuiltInTypeReferenceNode, isExternalTypeReferenceNode, isFunctionNode, isIntersectionNode, isLiteralNode, isObjectNode, isPrimitiveNode, isTupleNode, isTypeReferenceNode, isUnionNode, } from '@hubspot/ts-export-types-reader';
|
|
2
|
+
import { wrapCodeInHtmlCodeTags } from "./utils/jsx-utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* Renders an index signature node as a string representation.
|
|
5
|
+
*
|
|
6
|
+
* Converts an IndexSignatureNode into a TypeScript-like index signature string, e.g.:
|
|
7
|
+
* `[key: string]: any` or `readonly [key: number]: string`
|
|
8
|
+
*
|
|
9
|
+
* @param context - The rendering context containing the types reader
|
|
10
|
+
* @param indexSignature - The index signature node to render
|
|
11
|
+
* @returns A string representation of the index signature
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* renderIndexSignature(context, indexSignatureNode)
|
|
16
|
+
* // Returns: "[key: string]: any"
|
|
17
|
+
* // Returns: "readonly [key: number]: string"
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
const renderIndexSignature = (context, indexSignature) => {
|
|
21
|
+
const readonly = indexSignature.isReadonly ? 'readonly ' : '';
|
|
22
|
+
const keyType = renderType(context, indexSignature.keyType);
|
|
23
|
+
const valueType = renderType(context, indexSignature.valueType);
|
|
24
|
+
return `${readonly}[key: ${keyType}]: ${valueType}`;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Renders an object type node as a string representation.
|
|
28
|
+
*
|
|
29
|
+
* Converts an ObjectNode into a TypeScript-like object type string, e.g.:
|
|
30
|
+
* `{ name: string, age: number }` or `{ [key: string]: any, name: string }`
|
|
31
|
+
*
|
|
32
|
+
* If the object has index signatures, they are rendered before regular properties.
|
|
33
|
+
*
|
|
34
|
+
* @param context - The rendering context containing the types reader
|
|
35
|
+
* @param objectTypeNode - The object type node to render
|
|
36
|
+
* @returns A string representation of the object type
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* renderObjectType(context, objectNode)
|
|
41
|
+
* // Returns: "{ name: string, age: number }"
|
|
42
|
+
* // Returns: "{ [key: string]: any }"
|
|
43
|
+
* // Returns: "{ readonly [key: number]: string, [name: string]: string, regularProperty: hello }"
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
const renderObjectType = (context, objectTypeNode) => {
|
|
47
|
+
const { properties, indexSignatures } = objectTypeNode;
|
|
48
|
+
const hasIndexSignatures = indexSignatures && indexSignatures.length > 0;
|
|
49
|
+
const hasProperties = properties.length > 0;
|
|
50
|
+
// Empty object types render as just {}
|
|
51
|
+
if (!hasIndexSignatures && !hasProperties) {
|
|
52
|
+
return `{}`;
|
|
53
|
+
}
|
|
54
|
+
const parts = [];
|
|
55
|
+
// Render index signatures first
|
|
56
|
+
if (hasIndexSignatures) {
|
|
57
|
+
const indexParts = indexSignatures.map((indexSig) => renderIndexSignature(context, indexSig));
|
|
58
|
+
parts.push(...indexParts);
|
|
59
|
+
}
|
|
60
|
+
// Then render regular properties
|
|
61
|
+
if (hasProperties) {
|
|
62
|
+
const propertyParts = properties.map((property) => {
|
|
63
|
+
return `${property.name}: ${renderType(context, property.type)}`;
|
|
64
|
+
});
|
|
65
|
+
parts.push(...propertyParts);
|
|
66
|
+
}
|
|
67
|
+
return `{ ${parts.join(', ')} }`;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Renders an array type node as a string representation.
|
|
71
|
+
*
|
|
72
|
+
* Converts an ArrayNode into a TypeScript array type string, e.g.:
|
|
73
|
+
* `string[]` or `Array<string>`
|
|
74
|
+
*
|
|
75
|
+
* @param context - The rendering context containing the types reader
|
|
76
|
+
* @param arrayNode - The array type node to render
|
|
77
|
+
* @returns A string representation of the array type
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* renderArrayType(context, arrayNode)
|
|
82
|
+
* // Returns: "string[]" for simple types
|
|
83
|
+
* // Returns: "Array<"a" | "b">" for union types
|
|
84
|
+
* // Returns: "Array<A & B>" for intersection types
|
|
85
|
+
* // Returns: "Array<[string, number]>" for tuple types
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
const renderArrayType = (context, arrayNode) => {
|
|
89
|
+
const elementType = arrayNode.elementType;
|
|
90
|
+
const targetElementType = getTargetTypeNode(context, elementType);
|
|
91
|
+
// Use Array<T> syntax for union/intersection/tuple types to avoid ambiguity with operator precedence
|
|
92
|
+
// e.g., Array<"red" | "green" | "blue"> instead of ("red" | "green" | "blue")[]
|
|
93
|
+
// e.g., Array<A & B> instead of A & B[]
|
|
94
|
+
// e.g., Array<[string, number]> instead of [string, number][]
|
|
95
|
+
if (isUnionNode(targetElementType) ||
|
|
96
|
+
isIntersectionNode(targetElementType) ||
|
|
97
|
+
isTupleNode(targetElementType)) {
|
|
98
|
+
return `Array<${renderType(context, elementType)}>`;
|
|
99
|
+
}
|
|
100
|
+
// Use T[] syntax for simple types
|
|
101
|
+
return `${renderType(context, elementType)}[]`;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Renders a tuple type node as a string representation.
|
|
105
|
+
*
|
|
106
|
+
* Converts a TupleNode into a TypeScript tuple type string, e.g.:
|
|
107
|
+
* `[string, number]` or `[string, number, ...boolean[]]`
|
|
108
|
+
*
|
|
109
|
+
* @param context - The rendering context containing the types reader
|
|
110
|
+
* @param tupleNode - The tuple type node to render
|
|
111
|
+
* @returns A string representation of the tuple type
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* renderTupleType(context, tupleNode)
|
|
116
|
+
* // Returns: "[string, number]"
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
const renderTupleType = (context, tupleNode) => {
|
|
120
|
+
const elementParts = tupleNode.elements.map((element) => {
|
|
121
|
+
const renderedType = renderType(context, element.type);
|
|
122
|
+
// Handle rest elements (e.g., ...string[])
|
|
123
|
+
if (element.isRest) {
|
|
124
|
+
return `...${renderedType}`;
|
|
125
|
+
}
|
|
126
|
+
// Handle optional elements (e.g., string?)
|
|
127
|
+
if (element.isOptional) {
|
|
128
|
+
return `${renderedType}?`;
|
|
129
|
+
}
|
|
130
|
+
return renderedType;
|
|
131
|
+
});
|
|
132
|
+
return `[${elementParts.join(', ')}]`;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Renders a function type node as a string representation.
|
|
136
|
+
*
|
|
137
|
+
* Converts a FunctionNode into a TypeScript-like function type string, e.g.:
|
|
138
|
+
* `(param1: string, param2: number) => boolean`
|
|
139
|
+
*
|
|
140
|
+
* @param context - The rendering context containing the types reader
|
|
141
|
+
* @param functionNode - The function type node to render
|
|
142
|
+
* @returns A string representation of the function type
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* renderFunction(context, functionNode)
|
|
147
|
+
* // Returns: "(value: string) => boolean"
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
const renderFunction = (context, functionNode) => {
|
|
151
|
+
const paramParts = [];
|
|
152
|
+
// Render each parameter as "name: type"
|
|
153
|
+
for (const param of functionNode.parameters) {
|
|
154
|
+
paramParts.push(`${param.name}: ${renderType(context, param.type)}`);
|
|
155
|
+
}
|
|
156
|
+
// Combine parameters and return type into function signature format
|
|
157
|
+
return `(${paramParts.join(', ')}) => ${renderType(context, functionNode.returnType)}`;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Main type rendering function that converts an API node to a string representation.
|
|
161
|
+
*
|
|
162
|
+
* This is a recursive function that handles all TypeScript type constructs:
|
|
163
|
+
* - Primitives (string, number, boolean, etc.)
|
|
164
|
+
* - Literal types (e.g., "hello" | "world")
|
|
165
|
+
* - External type references (types from other packages)
|
|
166
|
+
* - Function types
|
|
167
|
+
* - Object types
|
|
168
|
+
* - Inlined type references
|
|
169
|
+
* - Built-in generic types (Array<T>, Promise<T>, etc.)
|
|
170
|
+
* - Type references (aliases and interfaces)
|
|
171
|
+
* - Union types
|
|
172
|
+
*
|
|
173
|
+
* The function uses the context to track the current path of type nodes being rendered,
|
|
174
|
+
* which allows it to detect actual circular references (where a type references itself
|
|
175
|
+
* in its own definition) while still expanding types that appear multiple times in
|
|
176
|
+
* different parts of the type tree.
|
|
177
|
+
*
|
|
178
|
+
* @param context - The rendering context containing the types reader and current type path
|
|
179
|
+
* @param typeNode - The API node representing the type to render
|
|
180
|
+
* @returns A string representation of the type (e.g., "string", "Array<number>", "string | number")
|
|
181
|
+
*/
|
|
182
|
+
const renderType = (context, typeNode) => {
|
|
183
|
+
// Handle primitive types (string, number, boolean, etc.)
|
|
184
|
+
if (isPrimitiveNode(typeNode)) {
|
|
185
|
+
return typeNode.name;
|
|
186
|
+
}
|
|
187
|
+
// Handle literal types (e.g., "hello", 42, true)
|
|
188
|
+
if (isLiteralNode(typeNode)) {
|
|
189
|
+
return JSON.stringify(typeNode.value);
|
|
190
|
+
}
|
|
191
|
+
// Handle external type references (types from other packages/modules)
|
|
192
|
+
// These are rendered as-is since we don't have their definitions
|
|
193
|
+
if (isExternalTypeReferenceNode(typeNode)) {
|
|
194
|
+
return typeNode.typeString;
|
|
195
|
+
}
|
|
196
|
+
// Handle array types
|
|
197
|
+
if (isArrayNode(typeNode)) {
|
|
198
|
+
return renderArrayType(context, typeNode);
|
|
199
|
+
}
|
|
200
|
+
// Handle tuple types
|
|
201
|
+
if (isTupleNode(typeNode)) {
|
|
202
|
+
return renderTupleType(context, typeNode);
|
|
203
|
+
}
|
|
204
|
+
// Handle function types
|
|
205
|
+
if (isFunctionNode(typeNode)) {
|
|
206
|
+
return renderFunction(context, typeNode);
|
|
207
|
+
}
|
|
208
|
+
// Handle object types
|
|
209
|
+
if (isObjectNode(typeNode)) {
|
|
210
|
+
return renderObjectType(context, typeNode);
|
|
211
|
+
}
|
|
212
|
+
// Handle built-in generic types (Array<T>, Promise<T>, etc.)
|
|
213
|
+
if (isBuiltInTypeReferenceNode(typeNode)) {
|
|
214
|
+
const { name, typeArguments } = typeNode;
|
|
215
|
+
// Render type arguments if present (e.g., <string, number>)
|
|
216
|
+
const renderedTypeArguments = typeArguments
|
|
217
|
+
? `<${typeArguments
|
|
218
|
+
.map((argType) => renderType(context, argType))
|
|
219
|
+
.join(', ')}>`
|
|
220
|
+
: '';
|
|
221
|
+
return `${name}${renderedTypeArguments}`;
|
|
222
|
+
}
|
|
223
|
+
// Handle type references (aliases, interfaces, etc.)
|
|
224
|
+
if (isTypeReferenceNode(typeNode)) {
|
|
225
|
+
// Look up the referenced type definition
|
|
226
|
+
const referencedNode = context.typesReader.findReferencedTypeById(typeNode.typeId);
|
|
227
|
+
// Check if this type node is already in the current path (actual recursion)
|
|
228
|
+
// If yes, return the type string instead of expanding to prevent infinite loop
|
|
229
|
+
if (context.typePath.includes(referencedNode)) {
|
|
230
|
+
return typeNode.typeString;
|
|
231
|
+
}
|
|
232
|
+
// Add to path before descending
|
|
233
|
+
context.typePath.push(referencedNode);
|
|
234
|
+
// Render the referenced type (may recurse through many paths)
|
|
235
|
+
const result = renderType(context, referencedNode);
|
|
236
|
+
// Remove from path after returning (ensures we pop even if renderType took complex paths)
|
|
237
|
+
context.typePath.pop();
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
240
|
+
// Handle union and intersection types (e.g., (string | number | boolean) and (A & B & C))
|
|
241
|
+
if (isUnionNode(typeNode) || isIntersectionNode(typeNode)) {
|
|
242
|
+
// Render each type in the union or intersection
|
|
243
|
+
const renderedParts = typeNode.types.map((type) => renderType(context, type));
|
|
244
|
+
// Sort parts alphabetically for consistent output
|
|
245
|
+
renderedParts.sort((a, b) => a.localeCompare(b));
|
|
246
|
+
const separator = isUnionNode(typeNode) ? '|' : '&';
|
|
247
|
+
return renderedParts.join(` ${separator} `);
|
|
248
|
+
}
|
|
249
|
+
throw new Error(`Unsupported type node: ${typeNode.kind}`);
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* Checks if a value is a non-empty array.
|
|
253
|
+
*
|
|
254
|
+
* @param array - The value to check
|
|
255
|
+
* @returns True if the value is an array with at least one element
|
|
256
|
+
*/
|
|
257
|
+
const isNonEmptyArray = (array) => Array.isArray(array) && array.length > 0;
|
|
258
|
+
/**
|
|
259
|
+
* Checks if a type node has type arguments (generic parameters).
|
|
260
|
+
*
|
|
261
|
+
* @param typeNode - The type node to check (must be InlinedTypeReferenceNode or TypeReferenceNode)
|
|
262
|
+
* @returns True if the type node has type arguments
|
|
263
|
+
*/
|
|
264
|
+
const hasTypeArgs = (typeNode) => isNonEmptyArray(typeNode.typeArguments);
|
|
265
|
+
/**
|
|
266
|
+
* Resolves a type node to its target type, handling type references and inlined references.
|
|
267
|
+
*
|
|
268
|
+
* For prop type rendering, we want to resolve type references to their actual definitions,
|
|
269
|
+
* but we want to preserve generic type arguments. This function:
|
|
270
|
+
* - For inlined type references: returns the node itself if it has type args, otherwise resolves to inner type
|
|
271
|
+
* - For type references: returns the node itself if it has type args, otherwise resolves to referenced type
|
|
272
|
+
* - For other nodes: returns the node as-is
|
|
273
|
+
*
|
|
274
|
+
* This ensures that generic types like `Array<string>` are preserved, while simple
|
|
275
|
+
* type aliases are resolved to their underlying types.
|
|
276
|
+
*
|
|
277
|
+
* @param context - The rendering context containing the types reader
|
|
278
|
+
* @param typeNode - The type node to resolve
|
|
279
|
+
* @returns The resolved target type node
|
|
280
|
+
*/
|
|
281
|
+
const getTargetTypeNode = (context, typeNode) => {
|
|
282
|
+
// For type references, preserve the node if it has type args (generics)
|
|
283
|
+
// Otherwise, look up and return the referenced type definition
|
|
284
|
+
if (isTypeReferenceNode(typeNode)) {
|
|
285
|
+
return hasTypeArgs(typeNode)
|
|
286
|
+
? typeNode
|
|
287
|
+
: context.typesReader.findReferencedTypeById(typeNode.typeId);
|
|
288
|
+
}
|
|
289
|
+
// For other node types, return as-is
|
|
290
|
+
return typeNode;
|
|
291
|
+
};
|
|
292
|
+
const renderPropTypeToJsxHelper = (context, propType) => {
|
|
293
|
+
// Resolve references to other types to the actual type node
|
|
294
|
+
// This handles type aliases and ensures generics are preserved
|
|
295
|
+
const targetPropType = getTargetTypeNode(context, propType);
|
|
296
|
+
// Special handling for union types: render each member recursively and sort
|
|
297
|
+
// This ensures nested unions are properly formatted
|
|
298
|
+
if (isUnionNode(targetPropType)) {
|
|
299
|
+
const renderedUnionTypes = targetPropType.types.map((currentType) => renderPropTypeToJsxHelper(context, currentType));
|
|
300
|
+
// Sort union parts alphabetically for consistent output
|
|
301
|
+
renderedUnionTypes.sort((a, b) => a.localeCompare(b));
|
|
302
|
+
return renderedUnionTypes.join(' | ');
|
|
303
|
+
}
|
|
304
|
+
return wrapCodeInHtmlCodeTags(renderType(context, targetPropType));
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* Renders a prop's type as an HTML string.
|
|
308
|
+
*
|
|
309
|
+
* This function handles the special case of union types in props, which need
|
|
310
|
+
* to be rendered recursively. All types are wrapped in HTML code tags for
|
|
311
|
+
* proper HTML rendering.
|
|
312
|
+
*
|
|
313
|
+
* @param context - The rendering context containing the types reader
|
|
314
|
+
* @param propType - The API node representing the prop's type
|
|
315
|
+
* @returns An HTML string representation of the type (wrapped in code tags)
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* ```typescript
|
|
319
|
+
* renderPropTypeToHtml(context, stringNode) // Returns: "<code>string</code>"
|
|
320
|
+
* renderPropTypeToHtml(context, unionNode) // Returns: "<code>boolean</code> | <code>number</code> | <code>string</code>"
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
export const renderPropTypeToJsx = (context, propType) => {
|
|
324
|
+
return `<>${renderPropTypeToJsxHelper(context, propType)}</>`;
|
|
325
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSDoc utilities for extracting metadata from JSDoc tags.
|
|
3
|
+
*
|
|
4
|
+
* This module provides helper functions for parsing and extracting information
|
|
5
|
+
* from JSDoc comments associated with component props, such as default values.
|
|
6
|
+
*/
|
|
7
|
+
import { JsdocTag } from '@hubspot/ts-export-types-reader';
|
|
8
|
+
/**
|
|
9
|
+
* Extracts the default value from JSDoc tags.
|
|
10
|
+
*
|
|
11
|
+
* Searches through an array of JSDoc tags to find a tag named 'defaultValue'
|
|
12
|
+
* and returns its text content. This is used to display default values in
|
|
13
|
+
* the component props documentation tables.
|
|
14
|
+
*
|
|
15
|
+
* @param tags - An array of JSDoc tags, or undefined if no tags exist
|
|
16
|
+
* @returns The default value text if found, or null if not found or if tags is undefined
|
|
17
|
+
*/
|
|
18
|
+
export declare const getDefaultValueFromJsdocTags: (tags: JsdocTag[] | undefined) => string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Extracts see tags from JSDoc tags.
|
|
21
|
+
*
|
|
22
|
+
* Searches through an array of JSDoc tags to find tags named 'see'
|
|
23
|
+
* and returns their text content. See tags are used to reference related
|
|
24
|
+
* documentation or external resources.
|
|
25
|
+
*
|
|
26
|
+
* @param tags - An array of JSDoc tags, or undefined if no tags exist
|
|
27
|
+
* @returns An array of see tag text values, or an empty array if none found
|
|
28
|
+
*/
|
|
29
|
+
export declare const getSeeTagsFromJsdocTags: (tags: JsdocTag[] | undefined) => string[];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSDoc utilities for extracting metadata from JSDoc tags.
|
|
3
|
+
*
|
|
4
|
+
* This module provides helper functions for parsing and extracting information
|
|
5
|
+
* from JSDoc comments associated with component props, such as default values.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Extracts the default value from JSDoc tags.
|
|
9
|
+
*
|
|
10
|
+
* Searches through an array of JSDoc tags to find a tag named 'defaultValue'
|
|
11
|
+
* and returns its text content. This is used to display default values in
|
|
12
|
+
* the component props documentation tables.
|
|
13
|
+
*
|
|
14
|
+
* @param tags - An array of JSDoc tags, or undefined if no tags exist
|
|
15
|
+
* @returns The default value text if found, or null if not found or if tags is undefined
|
|
16
|
+
*/
|
|
17
|
+
export const getDefaultValueFromJsdocTags = (tags) => {
|
|
18
|
+
// Return null if there are no tags
|
|
19
|
+
if (!tags) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
// Search through tags to find the defaultValue tag
|
|
23
|
+
for (const tag of tags) {
|
|
24
|
+
if (tag.name === 'defaultValue') {
|
|
25
|
+
// Return the tag's text, or null if text is empty/undefined
|
|
26
|
+
return tag.text || null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// No defaultValue tag found
|
|
30
|
+
return null;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Extracts see tags from JSDoc tags.
|
|
34
|
+
*
|
|
35
|
+
* Searches through an array of JSDoc tags to find tags named 'see'
|
|
36
|
+
* and returns their text content. See tags are used to reference related
|
|
37
|
+
* documentation or external resources.
|
|
38
|
+
*
|
|
39
|
+
* @param tags - An array of JSDoc tags, or undefined if no tags exist
|
|
40
|
+
* @returns An array of see tag text values, or an empty array if none found
|
|
41
|
+
*/
|
|
42
|
+
export const getSeeTagsFromJsdocTags = (tags) => {
|
|
43
|
+
// Return empty array if there are no tags
|
|
44
|
+
if (!tags) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
// Search through tags to find the @see tags and extract their text
|
|
48
|
+
// Filter out any tags with empty or undefined text
|
|
49
|
+
return tags
|
|
50
|
+
.filter((tag) => tag.name === 'see' && tag.text)
|
|
51
|
+
.map((tag) => tag.text);
|
|
52
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps code as text in HTML code tags for HTML rendering. Special HTML characters are escaped.
|
|
3
|
+
*
|
|
4
|
+
* @param value - The code as text to wrap in HTML code tags
|
|
5
|
+
* @returns The code as text wrapped in HTML code tags
|
|
6
|
+
*/
|
|
7
|
+
export declare const wrapCodeInHtmlCodeTags: (value: string) => string;
|
|
8
|
+
/**
|
|
9
|
+
* Converts HTML to XHTML (well-formed XML) by ensuring all void elements
|
|
10
|
+
* are self-closing and the output is valid XML.
|
|
11
|
+
*
|
|
12
|
+
* This is necessary because the generated HTML will be embedded in JSX
|
|
13
|
+
* fragments, which require valid XML syntax. For example, `<br>` must
|
|
14
|
+
* become `<br/>` and `<img src="...">` must become `<img src="..."/>`.
|
|
15
|
+
*
|
|
16
|
+
* Outermost `<p>` tags are stripped when the content is a single paragraph to allow
|
|
17
|
+
* for cleaner inline rendering. When multiple block elements exist (e.g., text followed
|
|
18
|
+
* by a list), the structure is preserved.
|
|
19
|
+
*
|
|
20
|
+
* @param html - The HTML string to convert
|
|
21
|
+
* @returns The HTML converted to XHTML format with self-closing tags
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* convertHtmlToJsx('<p>Hello<br>World</p>')
|
|
26
|
+
* // Returns: 'Hello<br/>World'
|
|
27
|
+
*
|
|
28
|
+
* convertHtmlToJsx('<img src="test.png">')
|
|
29
|
+
* // Returns: '<img src="test.png"/>'
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare const convertHtmlToJsx: (html: string) => string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { parseDocument, DomUtils } from 'htmlparser2';
|
|
2
|
+
import serialize from 'dom-serializer';
|
|
3
|
+
const escapeJsxExpressions = (jsx) => {
|
|
4
|
+
return jsx.replaceAll('{', '{').replaceAll('}', '}');
|
|
5
|
+
};
|
|
6
|
+
const sanitizeHtmlTextForCodeBlock = (htmlText) => {
|
|
7
|
+
return escapeJsxExpressions(htmlText
|
|
8
|
+
.replaceAll('&', '&')
|
|
9
|
+
.replaceAll('<', '<')
|
|
10
|
+
.replaceAll('>', '>'));
|
|
11
|
+
// NOTE: We are not putting HTML inside an HTML attribute so we don't need to escape quotes or apostrophes
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Wraps code as text in HTML code tags for HTML rendering. Special HTML characters are escaped.
|
|
15
|
+
*
|
|
16
|
+
* @param value - The code as text to wrap in HTML code tags
|
|
17
|
+
* @returns The code as text wrapped in HTML code tags
|
|
18
|
+
*/
|
|
19
|
+
export const wrapCodeInHtmlCodeTags = (value) => {
|
|
20
|
+
return `<code>${sanitizeHtmlTextForCodeBlock(value)}</code>`;
|
|
21
|
+
};
|
|
22
|
+
const stripOutermostParagraphNode = (dom) => {
|
|
23
|
+
// Check if there's exactly one root element and it's a <p> tag
|
|
24
|
+
const rootChildren = dom.children;
|
|
25
|
+
if (rootChildren.length !== 1) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const firstChild = rootChildren[0];
|
|
29
|
+
if (firstChild.type !== 'tag' || firstChild.name !== 'p') {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Move all of the children of the <p> element node to the root DOM node
|
|
33
|
+
const childrenToMove = [...firstChild.childNodes];
|
|
34
|
+
for (const child of childrenToMove) {
|
|
35
|
+
DomUtils.appendChild(dom, child);
|
|
36
|
+
}
|
|
37
|
+
// Now remove the <p> element node
|
|
38
|
+
DomUtils.removeElement(firstChild);
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Converts HTML to XHTML (well-formed XML) by ensuring all void elements
|
|
42
|
+
* are self-closing and the output is valid XML.
|
|
43
|
+
*
|
|
44
|
+
* This is necessary because the generated HTML will be embedded in JSX
|
|
45
|
+
* fragments, which require valid XML syntax. For example, `<br>` must
|
|
46
|
+
* become `<br/>` and `<img src="...">` must become `<img src="..."/>`.
|
|
47
|
+
*
|
|
48
|
+
* Outermost `<p>` tags are stripped when the content is a single paragraph to allow
|
|
49
|
+
* for cleaner inline rendering. When multiple block elements exist (e.g., text followed
|
|
50
|
+
* by a list), the structure is preserved.
|
|
51
|
+
*
|
|
52
|
+
* @param html - The HTML string to convert
|
|
53
|
+
* @returns The HTML converted to XHTML format with self-closing tags
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* convertHtmlToJsx('<p>Hello<br>World</p>')
|
|
58
|
+
* // Returns: 'Hello<br/>World'
|
|
59
|
+
*
|
|
60
|
+
* convertHtmlToJsx('<img src="test.png">')
|
|
61
|
+
* // Returns: '<img src="test.png"/>'
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export const convertHtmlToJsx = (html) => {
|
|
65
|
+
// Parse HTML into a DOM structure (parse as HTML to handle loose syntax)
|
|
66
|
+
const dom = parseDocument(html, {
|
|
67
|
+
xmlMode: false,
|
|
68
|
+
});
|
|
69
|
+
// Remove unnecessary outermost <p> tag
|
|
70
|
+
stripOutermostParagraphNode(dom);
|
|
71
|
+
// Serialize back to string with XML rules (self-closing tags, etc.)
|
|
72
|
+
const xhtml = serialize(dom, {
|
|
73
|
+
xmlMode: true, // Use XML/XHTML serialization rules
|
|
74
|
+
selfClosingTags: true, // Ensure void elements are self-closing
|
|
75
|
+
});
|
|
76
|
+
return `<>${escapeJsxExpressions(xhtml)}</>`;
|
|
77
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders markdown text to JSX.
|
|
3
|
+
*
|
|
4
|
+
* Supports both inline elements (emphasis, code spans, links) and block-level
|
|
5
|
+
* elements (lists, paragraphs, headings). The output is converted to XHTML format
|
|
6
|
+
* to ensure it's valid XML/JSX, with all void elements properly self-closing
|
|
7
|
+
* (e.g., `<br/>` instead of `<br>`).
|
|
8
|
+
*
|
|
9
|
+
* @param markdownText - The markdown text to render to HTML
|
|
10
|
+
* @returns The markdown text rendered to XHTML
|
|
11
|
+
*/
|
|
12
|
+
export declare const renderMarkdownToJsx: (markdownText: string) => string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Marked } from 'marked';
|
|
2
|
+
import { convertHtmlToJsx } from "./jsx-utils.js";
|
|
3
|
+
const markedInstance = new Marked({
|
|
4
|
+
breaks: true,
|
|
5
|
+
gfm: true,
|
|
6
|
+
});
|
|
7
|
+
/**
|
|
8
|
+
* Renders markdown text to JSX.
|
|
9
|
+
*
|
|
10
|
+
* Supports both inline elements (emphasis, code spans, links) and block-level
|
|
11
|
+
* elements (lists, paragraphs, headings). The output is converted to XHTML format
|
|
12
|
+
* to ensure it's valid XML/JSX, with all void elements properly self-closing
|
|
13
|
+
* (e.g., `<br/>` instead of `<br>`).
|
|
14
|
+
*
|
|
15
|
+
* @param markdownText - The markdown text to render to HTML
|
|
16
|
+
* @returns The markdown text rendered to XHTML
|
|
17
|
+
*/
|
|
18
|
+
export const renderMarkdownToJsx = (markdownText) => {
|
|
19
|
+
const html = markedInstance.parse(markdownText).toString().trim();
|
|
20
|
+
return convertHtmlToJsx(html);
|
|
21
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation for a component prop.
|
|
3
|
+
*/
|
|
4
|
+
export interface ComponentPropDocumentation {
|
|
5
|
+
/**
|
|
6
|
+
* The name of the prop.
|
|
7
|
+
*/
|
|
8
|
+
name: string;
|
|
9
|
+
/**
|
|
10
|
+
* Whether the prop is required.
|
|
11
|
+
*/
|
|
12
|
+
required: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* The type of the prop as a JSX code string (e.g., `"<><code>boolean</code></>"`).
|
|
15
|
+
*/
|
|
16
|
+
typeJsx: string;
|
|
17
|
+
/**
|
|
18
|
+
* The default value of the prop as a JSX code string (e.g., `"<><code>false</code></>"`).
|
|
19
|
+
*/
|
|
20
|
+
defaultValueJsx?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The description of the prop as a JSX code string (e.g., `"<>The description of the prop.</>"`).
|
|
23
|
+
*/
|
|
24
|
+
descriptionJsx: string;
|
|
25
|
+
/**
|
|
26
|
+
* The see items of the prop as JSX code strings (e.g., `["<><a href="https://example.com">Example</a></>"]`).
|
|
27
|
+
*
|
|
28
|
+
* These are typically markup for links to other documentation related to the prop.
|
|
29
|
+
*/
|
|
30
|
+
seeJsxItems?: string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The names of the components in the `@hubspot/ui-extensions` package.
|
|
34
|
+
*
|
|
35
|
+
* FUTURE: We should use code generation to generate this enum.
|
|
36
|
+
*/
|
|
37
|
+
export declare enum ComponentName {
|
|
38
|
+
Accordion = "Accordion",
|
|
39
|
+
Alert = "Alert",
|
|
40
|
+
AutoGrid = "AutoGrid",
|
|
41
|
+
BarChart = "BarChart",
|
|
42
|
+
Box = "Box",
|
|
43
|
+
Button = "Button",
|
|
44
|
+
ButtonRow = "ButtonRow",
|
|
45
|
+
Card = "Card",
|
|
46
|
+
Center = "Center",
|
|
47
|
+
Checkbox = "Checkbox",
|
|
48
|
+
CrmActionButton = "CrmActionButton",
|
|
49
|
+
CrmActionLink = "CrmActionLink",
|
|
50
|
+
CrmAssociationPivot = "CrmAssociationPivot",
|
|
51
|
+
CrmAssociationPropertyList = "CrmAssociationPropertyList",
|
|
52
|
+
CrmAssociationStageTracker = "CrmAssociationStageTracker",
|
|
53
|
+
CrmAssociationTable = "CrmAssociationTable",
|
|
54
|
+
CrmCardActions = "CrmCardActions",
|
|
55
|
+
CrmDataHighlight = "CrmDataHighlight",
|
|
56
|
+
CrmPropertyList = "CrmPropertyList",
|
|
57
|
+
CrmReport = "CrmReport",
|
|
58
|
+
CrmSimpleDeadline = "CrmSimpleDeadline",
|
|
59
|
+
CrmStageTracker = "CrmStageTracker",
|
|
60
|
+
CrmStatistics = "CrmStatistics",
|
|
61
|
+
CurrencyInput = "CurrencyInput",
|
|
62
|
+
DateInput = "DateInput",
|
|
63
|
+
DescriptionList = "DescriptionList",
|
|
64
|
+
DescriptionListItem = "DescriptionListItem",
|
|
65
|
+
Divider = "Divider",
|
|
66
|
+
Dropdown = "Dropdown",
|
|
67
|
+
DropdownButtonItem = "DropdownButtonItem",
|
|
68
|
+
EmptyState = "EmptyState",
|
|
69
|
+
ErrorState = "ErrorState",
|
|
70
|
+
ExpandableText = "ExpandableText",
|
|
71
|
+
FileInput = "FileInput",
|
|
72
|
+
Flex = "Flex",
|
|
73
|
+
Form = "Form",
|
|
74
|
+
Grid = "Grid",
|
|
75
|
+
GridItem = "GridItem",
|
|
76
|
+
HeaderActions = "HeaderActions",
|
|
77
|
+
Heading = "Heading",
|
|
78
|
+
Icon = "Icon",
|
|
79
|
+
Iframe = "Iframe",
|
|
80
|
+
Illustration = "Illustration",
|
|
81
|
+
Image = "Image",
|
|
82
|
+
Inline = "Inline",
|
|
83
|
+
Input = "Input",
|
|
84
|
+
LineChart = "LineChart",
|
|
85
|
+
Link = "Link",
|
|
86
|
+
List = "List",
|
|
87
|
+
LoadingButton = "LoadingButton",
|
|
88
|
+
LoadingSpinner = "LoadingSpinner",
|
|
89
|
+
MediaObject = "MediaObject",
|
|
90
|
+
Modal = "Modal",
|
|
91
|
+
ModalBody = "ModalBody",
|
|
92
|
+
ModalFooter = "ModalFooter",
|
|
93
|
+
MultiSelect = "MultiSelect",
|
|
94
|
+
NumberInput = "NumberInput",
|
|
95
|
+
Panel = "Panel",
|
|
96
|
+
PanelBody = "PanelBody",
|
|
97
|
+
PanelFooter = "PanelFooter",
|
|
98
|
+
PanelSection = "PanelSection",
|
|
99
|
+
Popover = "Popover",
|
|
100
|
+
PrimaryHeaderActionButton = "PrimaryHeaderActionButton",
|
|
101
|
+
ProgressBar = "ProgressBar",
|
|
102
|
+
RadioButton = "RadioButton",
|
|
103
|
+
ScoreCircle = "ScoreCircle",
|
|
104
|
+
SearchInput = "SearchInput",
|
|
105
|
+
SecondaryHeaderActionButton = "SecondaryHeaderActionButton",
|
|
106
|
+
Select = "Select",
|
|
107
|
+
SettingsView = "SettingsView",
|
|
108
|
+
Stack2 = "Stack2",
|
|
109
|
+
Statistics = "Statistics",
|
|
110
|
+
StatisticsItem = "StatisticsItem",
|
|
111
|
+
StatisticsTrend = "StatisticsTrend",
|
|
112
|
+
StatusTag = "StatusTag",
|
|
113
|
+
StepIndicator = "StepIndicator",
|
|
114
|
+
StepperInput = "StepperInput",
|
|
115
|
+
Tab = "Tab",
|
|
116
|
+
Table = "Table",
|
|
117
|
+
TableBody = "TableBody",
|
|
118
|
+
TableCell = "TableCell",
|
|
119
|
+
TableFooter = "TableFooter",
|
|
120
|
+
TableHead = "TableHead",
|
|
121
|
+
TableHeader = "TableHeader",
|
|
122
|
+
TableRow = "TableRow",
|
|
123
|
+
Tabs = "Tabs",
|
|
124
|
+
Tag = "Tag",
|
|
125
|
+
Text = "Text",
|
|
126
|
+
TextArea = "TextArea",
|
|
127
|
+
Tile = "Tile",
|
|
128
|
+
TimeInput = "TimeInput",
|
|
129
|
+
Toggle = "Toggle",
|
|
130
|
+
ToggleGroup = "ToggleGroup",
|
|
131
|
+
Tooltip = "Tooltip"
|
|
132
|
+
}
|