handoff-app 1.0.6 → 1.1.0-canary.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/Changelog.md CHANGED
@@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to
7
7
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## \[1.1.0]
10
+
9
11
  ## \[1.0.6] - 2026-02-24
10
12
 
11
13
  Version 1.0.4 fixed an issue when running Handoff in subpaths (eg.
package/dist/config.js CHANGED
@@ -9,6 +9,10 @@ const defaultConfig = () => {
9
9
  exportsOutputDirectory: (_c = process.env.HANDOFF_OUTPUT_DIR) !== null && _c !== void 0 ? _c : 'exported',
10
10
  sitesOutputDirectory: (_d = process.env.HANDOFF_SITES_DIR) !== null && _d !== void 0 ? _d : 'out',
11
11
  useVariables: (_e = Boolean(process.env.HANDOFF_USE_VARIABLES)) !== null && _e !== void 0 ? _e : false,
12
+ reactDocgen: {
13
+ maxDepth: 7,
14
+ excludeDirectories: ['dist', 'build', '.next'],
15
+ },
12
16
  app: {
13
17
  theme: 'default',
14
18
  title: 'Convertiv Design System',
@@ -1,9 +1,16 @@
1
- /**
2
- * Generates component properties using react-docgen-typescript
3
- * @param entry - Path to the component/schema file
4
- * @param handoff - Handoff instance for configuration
5
- * @returns Generated properties or null if failed
6
- */
1
+ import { type GeneratedDocs } from 'handoff-docgen';
2
+ import { SlotMetadata } from '../preview/component';
3
+ export declare const getPropertiesFromGeneratedDocs: (generatedDocs: GeneratedDocs, entry: string, handoff: any) => {
4
+ [key: string]: SlotMetadata;
5
+ } | null;
6
+ export declare const enrichPropertiesWithDocgen: (properties: {
7
+ [key: string]: SlotMetadata;
8
+ } | null | undefined, docgenProperties: {
9
+ [key: string]: SlotMetadata;
10
+ } | null | undefined) => {
11
+ [key: string]: SlotMetadata;
12
+ } | null;
13
+ export declare const generateDocsArtifact: (entry: string, handoff: any) => Promise<GeneratedDocs | null>;
7
14
  export declare const generatePropertiesFromDocgen: (entry: string, handoff: any) => Promise<{
8
15
  [key: string]: any;
9
16
  } | null>;
@@ -12,50 +12,223 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.generatePropertiesFromDocgen = void 0;
15
+ exports.generatePropertiesFromDocgen = exports.generateDocsArtifact = exports.enrichPropertiesWithDocgen = exports.getPropertiesFromGeneratedDocs = void 0;
16
16
  const fs_extra_1 = __importDefault(require("fs-extra"));
17
17
  const path_1 = __importDefault(require("path"));
18
- const react_docgen_typescript_1 = require("react-docgen-typescript");
18
+ const handoff_docgen_1 = require("handoff-docgen");
19
19
  const logger_1 = require("../../utils/logger");
20
- const schema_1 = require("../utils/schema");
21
- /**
22
- * Generates component properties using react-docgen-typescript
23
- * @param entry - Path to the component/schema file
24
- * @param handoff - Handoff instance for configuration
25
- * @returns Generated properties or null if failed
26
- */
27
- const generatePropertiesFromDocgen = (entry, handoff) => __awaiter(void 0, void 0, void 0, function* () {
20
+ const component_1 = require("../preview/component");
21
+ const DEFAULT_DOCGEN_MAX_DEPTH = 7;
22
+ const DEFAULT_DOCGEN_EXCLUDE_DIRECTORIES = ['dist', 'build', '.next'];
23
+ const generatorCache = new Map();
24
+ const normalizePathForCompare = (filePath) => filePath.replace(/\\/g, '/');
25
+ const mapDocgenTypeToSlotType = (typeName) => {
26
+ const normalizedType = typeName === null || typeName === void 0 ? void 0 : typeName.trim().toLowerCase();
27
+ switch (normalizedType) {
28
+ case 'boolean':
29
+ return component_1.SlotType.BOOLEAN;
30
+ case 'number':
31
+ return component_1.SlotType.NUMBER;
32
+ case 'string':
33
+ return component_1.SlotType.TEXT;
34
+ case 'array':
35
+ return component_1.SlotType.ARRAY;
36
+ case 'object':
37
+ return component_1.SlotType.OBJECT;
38
+ case 'function':
39
+ return component_1.SlotType.FUNCTION;
40
+ case 'enum':
41
+ return component_1.SlotType.ENUM;
42
+ case 'any':
43
+ case 'unknown':
44
+ return component_1.SlotType.ANY;
45
+ default:
46
+ if (normalizedType === null || normalizedType === void 0 ? void 0 : normalizedType.includes('=>')) {
47
+ return component_1.SlotType.FUNCTION;
48
+ }
49
+ return component_1.SlotType.TEXT;
50
+ }
51
+ };
52
+ const getDocgenConfig = (handoff) => {
53
+ var _a, _b, _c;
54
+ const reactDocgenConfig = (_b = (_a = handoff === null || handoff === void 0 ? void 0 : handoff.config) === null || _a === void 0 ? void 0 : _a.reactDocgen) !== null && _b !== void 0 ? _b : (_c = handoff === null || handoff === void 0 ? void 0 : handoff.config) === null || _c === void 0 ? void 0 : _c.docgen;
55
+ const configuredMaxDepth = reactDocgenConfig === null || reactDocgenConfig === void 0 ? void 0 : reactDocgenConfig.maxDepth;
56
+ const configuredExcludeDirectories = reactDocgenConfig === null || reactDocgenConfig === void 0 ? void 0 : reactDocgenConfig.excludeDirectories;
57
+ const maxDepth = typeof configuredMaxDepth === 'number' && Number.isFinite(configuredMaxDepth) && configuredMaxDepth > 0
58
+ ? Math.floor(configuredMaxDepth)
59
+ : DEFAULT_DOCGEN_MAX_DEPTH;
60
+ const excludeDirectories = Array.isArray(configuredExcludeDirectories)
61
+ ? configuredExcludeDirectories
62
+ .map((entry) => String(entry).trim())
63
+ .filter((entry) => entry.length > 0)
64
+ : DEFAULT_DOCGEN_EXCLUDE_DIRECTORIES;
65
+ return {
66
+ maxDepth,
67
+ excludeDirectories: excludeDirectories.length > 0 ? excludeDirectories : DEFAULT_DOCGEN_EXCLUDE_DIRECTORIES,
68
+ };
69
+ };
70
+ const getGenerator = (componentDirectory, tsconfigPath, maxDepth, excludeDirectories) => {
71
+ const cacheKey = `${componentDirectory}::${tsconfigPath}::${maxDepth}::${excludeDirectories.join('|')}`;
72
+ const cached = generatorCache.get(cacheKey);
73
+ if (cached) {
74
+ return cached;
75
+ }
76
+ const generator = (0, handoff_docgen_1.createGenerator)({
77
+ componentDirectory,
78
+ tsconfigPath,
79
+ maxDepth,
80
+ pathMode: 'relative',
81
+ excludeDirectories,
82
+ });
83
+ generatorCache.set(cacheKey, generator);
84
+ return generator;
85
+ };
86
+ const findComponentDocForEntry = (generatedDocs, entry, handoff) => {
87
+ const normalizedEntryAbsolute = normalizePathForCompare(path_1.default.resolve(entry));
88
+ const normalizedEntryRelative = normalizePathForCompare(path_1.default.relative(handoff.workingPath, normalizedEntryAbsolute));
89
+ const entryBasename = path_1.default.basename(normalizedEntryAbsolute, path_1.default.extname(normalizedEntryAbsolute));
90
+ const matchesByFile = generatedDocs.components.filter((component) => {
91
+ const source = normalizePathForCompare(component.sourceFile);
92
+ return source === normalizedEntryAbsolute || source === normalizedEntryRelative || source.endsWith(`/${normalizedEntryRelative}`);
93
+ });
94
+ if (matchesByFile.length > 0) {
95
+ return matchesByFile[0];
96
+ }
97
+ const matchesByName = generatedDocs.components.filter((component) => component.name === entryBasename);
98
+ if (matchesByName.length > 0) {
99
+ return matchesByName[0];
100
+ }
101
+ return generatedDocs.components[0];
102
+ };
103
+ const toSlotMetadata = (prop) => {
104
+ var _a, _b;
105
+ const slotType = mapDocgenTypeToSlotType(prop.docgenType || ((_a = prop.deepType) === null || _a === void 0 ? void 0 : _a.kind) || 'any');
106
+ const deepType = prop.deepType;
107
+ const metadata = {
108
+ id: prop.name,
109
+ name: prop.name,
110
+ description: prop.description || '',
111
+ generic: (deepType === null || deepType === void 0 ? void 0 : deepType.display) || prop.docgenType || '',
112
+ default: prop.defaultValue,
113
+ type: slotType,
114
+ rules: {
115
+ required: !!prop.required,
116
+ },
117
+ docgenType: prop.docgenType,
118
+ deepType: prop.deepType,
119
+ typeRefs: prop.typeRefs,
120
+ warnings: prop.warnings,
121
+ annotations: prop.annotations,
122
+ };
123
+ if ((deepType === null || deepType === void 0 ? void 0 : deepType.kind) === 'object' && ((_b = deepType.properties) === null || _b === void 0 ? void 0 : _b.length)) {
124
+ metadata.type = component_1.SlotType.OBJECT;
125
+ metadata.properties = Object.fromEntries(deepType.properties.map((property) => {
126
+ var _a, _b;
127
+ return [
128
+ property.name,
129
+ toSlotMetadata({
130
+ name: property.name,
131
+ required: !property.optional,
132
+ description: property.description,
133
+ docgenType: ((_a = property.type) === null || _a === void 0 ? void 0 : _a.display) || ((_b = property.type) === null || _b === void 0 ? void 0 : _b.kind) || 'unknown',
134
+ deepType: property.type,
135
+ annotations: property.annotations,
136
+ }),
137
+ ];
138
+ }));
139
+ }
140
+ else if ((deepType === null || deepType === void 0 ? void 0 : deepType.kind) === 'array' && deepType.elementType) {
141
+ metadata.type = component_1.SlotType.ARRAY;
142
+ metadata.items = {
143
+ type: mapDocgenTypeToSlotType(deepType.elementType.display || deepType.elementType.kind),
144
+ properties: deepType.elementType.kind === 'object' && deepType.elementType.properties
145
+ ? Object.fromEntries(deepType.elementType.properties.map((property) => {
146
+ var _a, _b;
147
+ return [
148
+ property.name,
149
+ toSlotMetadata({
150
+ name: property.name,
151
+ required: !property.optional,
152
+ description: property.description,
153
+ docgenType: ((_a = property.type) === null || _a === void 0 ? void 0 : _a.display) || ((_b = property.type) === null || _b === void 0 ? void 0 : _b.kind) || 'unknown',
154
+ deepType: property.type,
155
+ annotations: property.annotations,
156
+ }),
157
+ ];
158
+ }))
159
+ : undefined,
160
+ };
161
+ }
162
+ return metadata;
163
+ };
164
+ const toPropertiesMap = (generatedDocs, entry, handoff) => {
165
+ var _a;
166
+ if (!generatedDocs.components.length) {
167
+ return null;
168
+ }
169
+ const componentDoc = findComponentDocForEntry(generatedDocs, entry, handoff);
170
+ if (!componentDoc || !((_a = componentDoc.props) === null || _a === void 0 ? void 0 : _a.length)) {
171
+ return null;
172
+ }
173
+ const properties = {};
174
+ for (const prop of componentDoc.props) {
175
+ properties[prop.name] = toSlotMetadata(prop);
176
+ }
177
+ return properties;
178
+ };
179
+ const getPropertiesFromGeneratedDocs = (generatedDocs, entry, handoff) => toPropertiesMap(generatedDocs, entry, handoff);
180
+ exports.getPropertiesFromGeneratedDocs = getPropertiesFromGeneratedDocs;
181
+ const enrichPropertiesWithDocgen = (properties, docgenProperties) => {
182
+ if (!properties && !docgenProperties) {
183
+ return null;
184
+ }
185
+ if (!properties) {
186
+ return docgenProperties || null;
187
+ }
188
+ if (!docgenProperties) {
189
+ return properties;
190
+ }
191
+ const merged = Object.assign({}, properties);
192
+ const docgenEntries = Object.entries(docgenProperties);
193
+ for (const [propName, docgenProp] of docgenEntries) {
194
+ const existing = merged[propName];
195
+ if (!existing) {
196
+ merged[propName] = docgenProp;
197
+ continue;
198
+ }
199
+ merged[propName] = Object.assign(Object.assign({}, existing), { generic: existing.generic || docgenProp.generic, default: existing.default || docgenProp.default, docgenType: docgenProp.docgenType, deepType: docgenProp.deepType, typeRefs: docgenProp.typeRefs, warnings: docgenProp.warnings, annotations: docgenProp.annotations });
200
+ }
201
+ return merged;
202
+ };
203
+ exports.enrichPropertiesWithDocgen = enrichPropertiesWithDocgen;
204
+ const generateDocsArtifact = (entry, handoff) => __awaiter(void 0, void 0, void 0, function* () {
28
205
  try {
29
- // Use root project's tsconfig.json
30
206
  const tsconfigPath = path_1.default.resolve(handoff.workingPath, 'tsconfig.json');
31
- // Check if tsconfig exists
32
207
  if (!fs_extra_1.default.existsSync(tsconfigPath)) {
33
- logger_1.Logger.warn(`TypeScript config not found at ${tsconfigPath}, using default configuration`);
208
+ logger_1.Logger.warn(`TypeScript config not found at ${tsconfigPath}, using default resolution`);
34
209
  }
35
- const parserConfig = {
36
- savePropValueAsString: true,
37
- shouldExtractLiteralValuesFromEnum: true,
38
- shouldRemoveUndefinedFromOptional: true,
39
- propFilter: (prop) => {
40
- if (prop.parent) {
41
- return !prop.parent.fileName.includes('node_modules');
42
- }
43
- return true;
44
- },
45
- };
46
- const parser = (0, react_docgen_typescript_1.withCustomConfig)(tsconfigPath, parserConfig);
47
- const docgenResults = parser.parse(entry);
48
- if (docgenResults.length > 0) {
49
- const componentDoc = docgenResults[0];
50
- if (componentDoc.props && Object.keys(componentDoc.props).length > 0) {
51
- return (0, schema_1.convertDocgenToProperties)(Object.values(componentDoc.props));
52
- }
53
- }
54
- return null;
210
+ const componentDirectory = path_1.default.dirname(path_1.default.resolve(entry));
211
+ const { maxDepth, excludeDirectories } = getDocgenConfig(handoff);
212
+ const generator = getGenerator(componentDirectory, tsconfigPath, maxDepth, excludeDirectories);
213
+ return yield generator.generate({
214
+ componentDirectory,
215
+ tsconfigPath,
216
+ maxDepth,
217
+ pathMode: 'relative',
218
+ excludeDirectories,
219
+ });
55
220
  }
56
221
  catch (error) {
57
- logger_1.Logger.warn(`Failed to generate docs with react-docgen-typescript for ${entry}: ${error}`);
222
+ logger_1.Logger.warn(`Failed to generate docs with handoff-docgen for ${entry}: ${error}`);
223
+ return null;
224
+ }
225
+ });
226
+ exports.generateDocsArtifact = generateDocsArtifact;
227
+ const generatePropertiesFromDocgen = (entry, handoff) => __awaiter(void 0, void 0, void 0, function* () {
228
+ const generatedDocs = yield (0, exports.generateDocsArtifact)(entry, handoff);
229
+ if (!generatedDocs) {
58
230
  return null;
59
231
  }
232
+ return (0, exports.getPropertiesFromGeneratedDocs)(generatedDocs, entry, handoff);
60
233
  });
61
234
  exports.generatePropertiesFromDocgen = generatePropertiesFromDocgen;
@@ -62,7 +62,7 @@ function loadComponentSchemaAndModule(componentData, componentPath, handoff) {
62
62
  component = moduleExports.exports.default;
63
63
  // Try to load schema from component exports
64
64
  properties = yield (0, schema_loader_1.loadSchemaFromComponent)(moduleExports.exports, handoff);
65
- // If no schema found, use react-docgen-typescript
65
+ // If no schema found, use shared docgen fallback
66
66
  if (!properties) {
67
67
  properties = yield (0, docgen_1.generatePropertiesFromDocgen)(componentPath, handoff);
68
68
  }
@@ -169,6 +169,7 @@ function ssrRenderPlugin(componentData, documentationComponents, handoff) {
169
169
  const componentSourceCode = fs_extra_1.default.readFileSync(componentPath, 'utf8');
170
170
  // Load component schema and module
171
171
  const [schemaProperties, ReactComponent] = yield loadComponentSchemaAndModule(componentData, componentPath, handoff);
172
+ const generatedDocs = yield (0, docgen_1.generateDocsArtifact)(componentPath, handoff);
172
173
  if (!ReactComponent) {
173
174
  logger_1.Logger.error(`Failed to load React component for ${componentId}`);
174
175
  return;
@@ -177,6 +178,11 @@ function ssrRenderPlugin(componentData, documentationComponents, handoff) {
177
178
  if (schemaProperties) {
178
179
  componentData.properties = schemaProperties;
179
180
  }
181
+ if (generatedDocs) {
182
+ const docgenProperties = (0, docgen_1.getPropertiesFromGeneratedDocs)(generatedDocs, componentPath, handoff);
183
+ componentData.properties = (0, docgen_1.enrichPropertiesWithDocgen)(componentData.properties, docgenProperties) || {};
184
+ componentData.docgen = generatedDocs;
185
+ }
180
186
  // Ensure components object exists
181
187
  if (!documentationComponents) {
182
188
  documentationComponents = {};
@@ -41,6 +41,7 @@ declare const buildComponentCss: (data: TransformComponentTokensResult, handoff:
41
41
  };
42
42
  validations?: Record<string, import("../../../types").ValidationResult>;
43
43
  page?: import("../types").ComponentPageDefinition;
44
+ docgen?: import("handoff-docgen").GeneratedDocs;
44
45
  }>;
45
46
  /**
46
47
  * Build the main CSS file using Vite
@@ -1,3 +1,4 @@
1
+ import type { DocAnnotation, TypeNode } from 'handoff-docgen';
1
2
  import Handoff from '../../index';
2
3
  export interface ComponentMetadata {
3
4
  title: string;
@@ -38,6 +39,11 @@ export interface SlotMetadata {
38
39
  };
39
40
  key?: string;
40
41
  rules?: RuleObject;
42
+ docgenType?: string;
43
+ deepType?: TypeNode;
44
+ typeRefs?: string[];
45
+ warnings?: string[];
46
+ annotations?: DocAnnotation[];
41
47
  }
42
48
  export type RuleObject = {
43
49
  required?: boolean;
@@ -1,4 +1,5 @@
1
1
  import { Card } from '../../app/components/Component/Cards';
2
+ import type { GeneratedDocs } from 'handoff-docgen';
2
3
  import { ValidationResult } from '../../types';
3
4
  import { Filter } from '../../utils/filter';
4
5
  import { SlotMetadata } from './component';
@@ -159,6 +160,7 @@ export type TransformComponentTokensResult = {
159
160
  };
160
161
  validations?: Record<string, ValidationResult>;
161
162
  page?: ComponentPageDefinition;
163
+ docgen?: GeneratedDocs;
162
164
  } | null;
163
165
  export type OptionalPreviewRender = {
164
166
  title: string;
@@ -2,24 +2,6 @@ import { Types as CoreTypes } from 'handoff-core';
2
2
  import { Plugin } from 'vite';
3
3
  import { SlotMetadata } from './preview/component';
4
4
  import { TransformComponentTokensResult } from './preview/types';
5
- /**
6
- * Configuration for react-docgen-typescript parser
7
- */
8
- export interface DocgenParserConfig {
9
- savePropValueAsString: boolean;
10
- shouldExtractLiteralValuesFromEnum: boolean;
11
- shouldRemoveUndefinedFromOptional: boolean;
12
- propFilter: (prop: any) => boolean;
13
- }
14
- /**
15
- * Result from react-docgen-typescript parsing
16
- */
17
- export interface DocgenResult {
18
- props: {
19
- [key: string]: any;
20
- };
21
- [key: string]: any;
22
- }
23
5
  /**
24
6
  * Module evaluation result
25
7
  */
@@ -43,7 +43,7 @@ const loadSchemaFromFile = (schemaPath, handoff) => __awaiter(void 0, void 0, vo
43
43
  }
44
44
  else if (schema) {
45
45
  // Schema exists but is not a valid schema object (e.g., type/interface)
46
- // Use react-docgen-typescript to document the schema file
46
+ // Use handoff-docgen to document the schema file
47
47
  return yield (0, docgen_1.generatePropertiesFromDocgen)(schemaPath, handoff);
48
48
  }
49
49
  return null;
@@ -72,7 +72,7 @@ const loadSchemaFromComponent = (componentExports, handoff) => __awaiter(void 0,
72
72
  }
73
73
  else if (schema) {
74
74
  // Schema exists but is not a valid schema object (e.g., type/interface)
75
- // Use react-docgen-typescript to document the schema
75
+ // Use handoff-docgen to document the schema
76
76
  return yield (0, docgen_1.generatePropertiesFromDocgen)(componentExports.__filename || '', handoff);
77
77
  }
78
78
  return null;
@@ -10,8 +10,8 @@ export declare const ensureIds: (properties: {
10
10
  [key: string]: SlotMetadata;
11
11
  };
12
12
  /**
13
- * Converts react-docgen-typescript props to our SlotMetadata format
14
- * @param docgenProps - Array of props from react-docgen-typescript
13
+ * Converts baseline docgen props to our SlotMetadata format
14
+ * @param docgenProps - Array of props from a docgen parser
15
15
  * @returns Converted properties object
16
16
  */
17
17
  export declare const convertDocgenToProperties: (docgenProps: any[]) => {
@@ -470,8 +470,8 @@ const ensureNestedParsing = (metadata) => {
470
470
  return metadata;
471
471
  };
472
472
  /**
473
- * Converts react-docgen-typescript props to our SlotMetadata format
474
- * @param docgenProps - Array of props from react-docgen-typescript
473
+ * Converts baseline docgen props to our SlotMetadata format
474
+ * @param docgenProps - Array of props from a docgen parser
475
475
  * @returns Converted properties object
476
476
  */
477
477
  const convertDocgenToProperties = (docgenProps) => {
@@ -74,6 +74,21 @@ export interface Config {
74
74
  exportsOutputDirectory?: string;
75
75
  sitesOutputDirectory?: string;
76
76
  useVariables?: boolean;
77
+ /**
78
+ * Configuration for React component docs generation (handoff-docgen).
79
+ */
80
+ reactDocgen?: {
81
+ /**
82
+ * Maximum recursion depth for nested type traversal.
83
+ * @default 7
84
+ */
85
+ maxDepth?: number;
86
+ /**
87
+ * Directory names to exclude while scanning for components.
88
+ * @default ["dist", "build", ".next"]
89
+ */
90
+ excludeDirectories?: string[];
91
+ };
77
92
  app?: NextAppConfig;
78
93
  /**
79
94
  * Configuration for the build pipeline
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Types as CoreTypes } from 'handoff-core';
2
+ import type { GeneratedDocs } from 'handoff-docgen';
2
3
  import { SlotMetadata } from './transformers/preview/component';
3
4
  import { ComponentPageDefinition } from './transformers/preview/types';
4
5
  import { type Filter } from './utils/filter';
@@ -74,6 +75,7 @@ export interface PreviewObject {
74
75
  */
75
76
  validations?: Record<string, ValidationResult>;
76
77
  page?: ComponentPageDefinition;
78
+ docgen?: GeneratedDocs;
77
79
  }
78
80
  export type PreviewJson = {
79
81
  components: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "handoff-app",
3
- "version": "1.0.6",
3
+ "version": "1.1.0-canary.1",
4
4
  "description": "Automated documentation toolchain for building client side documentation from figma",
5
5
  "author ": {
6
6
  "name": "Convertiv",
@@ -100,6 +100,7 @@
100
100
  "gray-matter": "^4.0.3",
101
101
  "handlebars": "^4.7.8",
102
102
  "handoff-core": "^0.2.0",
103
+ "handoff-docgen": "^0.1.0",
103
104
  "html-react-parser": "^5.2.5",
104
105
  "jquery": "^3.6.1",
105
106
  "lodash": "^4.17.21",