handoff-app 1.1.3 → 1.2.0-0

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.
Files changed (143) hide show
  1. package/config/config.template.js +21 -14
  2. package/config/config.template.ts +54 -0
  3. package/config/docs/system.md +2 -0
  4. package/dist/app-builder/build.js +7 -2
  5. package/dist/app-builder/config-diff/index.d.ts +4 -0
  6. package/dist/app-builder/config-diff/index.js +12 -0
  7. package/dist/app-builder/config-diff/registry.d.ts +9 -0
  8. package/dist/app-builder/config-diff/registry.js +30 -0
  9. package/dist/app-builder/config-diff/snapshot.d.ts +13 -0
  10. package/dist/app-builder/config-diff/snapshot.js +43 -0
  11. package/dist/app-builder/config-diff/strategies/pattern.d.ts +2 -0
  12. package/dist/app-builder/config-diff/strategies/pattern.js +93 -0
  13. package/dist/app-builder/config-diff/types.d.ts +19 -0
  14. package/dist/app-builder/config-diff/types.js +2 -0
  15. package/dist/app-builder/watchers/component.d.ts +15 -0
  16. package/dist/app-builder/watchers/component.js +69 -0
  17. package/dist/app-builder/watchers/utils.d.ts +14 -0
  18. package/dist/app-builder/watchers/utils.js +44 -0
  19. package/dist/app-builder/watchers.d.ts +10 -19
  20. package/dist/app-builder/watchers.js +56 -102
  21. package/dist/cache/build-cache.js +14 -5
  22. package/dist/cli/create.js +67 -23
  23. package/dist/cli/eject.js +48 -7
  24. package/dist/cli/make.js +29 -4
  25. package/dist/cli/scaffold.js +60 -19
  26. package/dist/cli.js +1 -1
  27. package/dist/config/helpers.d.ts +7 -0
  28. package/dist/config/helpers.js +31 -0
  29. package/dist/config/index.d.ts +2 -1
  30. package/dist/config/index.js +5 -1
  31. package/dist/config/loader.d.ts +10 -1
  32. package/dist/config/loader.js +92 -28
  33. package/dist/config/normalizers/declaration.d.ts +11 -0
  34. package/dist/config/normalizers/declaration.js +119 -0
  35. package/dist/config/normalizers/pattern.d.ts +9 -0
  36. package/dist/config/normalizers/pattern.js +38 -0
  37. package/dist/config/runtime.d.ts +9 -3
  38. package/dist/config/runtime.js +254 -33
  39. package/dist/declarations/helpers.d.ts +6 -0
  40. package/dist/declarations/helpers.js +19 -0
  41. package/dist/declarations/index.d.ts +2 -0
  42. package/dist/declarations/index.js +9 -0
  43. package/dist/declarations/types.d.ts +60 -0
  44. package/dist/declarations/types.js +2 -0
  45. package/dist/index.d.ts +12 -1
  46. package/dist/index.js +34 -5
  47. package/dist/pipeline/index.d.ts +1 -0
  48. package/dist/pipeline/index.js +3 -1
  49. package/dist/pipeline/patterns.d.ts +6 -0
  50. package/dist/pipeline/patterns.js +21 -0
  51. package/dist/transformers/docgen/index.js +12 -2
  52. package/dist/transformers/plugins/csf-render.js +170 -34
  53. package/dist/transformers/plugins/ssr-render.js +18 -2
  54. package/dist/transformers/preview/component/api.js +16 -7
  55. package/dist/transformers/preview/component/builder.js +9 -3
  56. package/dist/transformers/preview/component/css.d.ts +8 -0
  57. package/dist/transformers/preview/component/html.js +14 -5
  58. package/dist/transformers/preview/component/previews.d.ts +9 -0
  59. package/dist/transformers/preview/component/previews.js +10 -0
  60. package/dist/transformers/preview/pattern/api.d.ts +7 -0
  61. package/dist/transformers/preview/pattern/api.js +61 -0
  62. package/dist/transformers/preview/pattern/builder.d.ts +9 -0
  63. package/dist/transformers/preview/pattern/builder.js +86 -0
  64. package/dist/transformers/preview/pattern/html.d.ts +19 -0
  65. package/dist/transformers/preview/pattern/html.js +94 -0
  66. package/dist/transformers/preview/types.d.ts +54 -0
  67. package/dist/transformers/utils/csf.d.ts +16 -0
  68. package/dist/transformers/utils/csf.js +30 -0
  69. package/dist/transformers/utils/index.d.ts +1 -0
  70. package/dist/transformers/utils/index.js +2 -0
  71. package/dist/transformers/utils/module.d.ts +5 -0
  72. package/dist/transformers/utils/module.js +26 -13
  73. package/dist/transformers/utils/usage.d.ts +23 -0
  74. package/dist/transformers/utils/usage.js +294 -0
  75. package/dist/types/config.d.ts +32 -7
  76. package/dist/types/preview.d.ts +2 -0
  77. package/docs/cli.md +3 -3
  78. package/package.json +3 -1
  79. package/src/app/components/Component/PageSliceResolver.tsx +6 -6
  80. package/src/app/components/Component/Preview.tsx +17 -12
  81. package/src/app/components/Component/slices/ComponentDisplaySlice.tsx +16 -6
  82. package/src/app/components/Markdown/CodeHighlight.tsx +60 -11
  83. package/src/app/components/util/index.ts +71 -2
  84. package/src/app/pages/system/pattern/[pattern]/index.tsx +223 -0
  85. package/src/app/pages/system/pattern/index.tsx +14 -0
  86. package/src/app-builder/README.md +37 -9
  87. package/src/app-builder/build.ts +8 -2
  88. package/src/app-builder/config-diff/index.ts +4 -0
  89. package/src/app-builder/config-diff/registry.ts +23 -0
  90. package/src/app-builder/config-diff/snapshot.ts +51 -0
  91. package/src/app-builder/config-diff/strategies/pattern.ts +65 -0
  92. package/src/app-builder/config-diff/types.ts +21 -0
  93. package/src/app-builder/watchers/component.ts +72 -0
  94. package/src/app-builder/watchers/utils.ts +44 -0
  95. package/src/app-builder/watchers.ts +62 -80
  96. package/src/cache/build-cache.ts +14 -5
  97. package/src/cli/create.ts +86 -25
  98. package/src/cli/eject.ts +29 -7
  99. package/src/cli/make.ts +32 -5
  100. package/src/cli/scaffold.ts +68 -21
  101. package/src/cli.ts +1 -1
  102. package/src/config/README.md +10 -1
  103. package/src/config/helpers.ts +43 -0
  104. package/src/config/index.ts +4 -1
  105. package/src/config/loader.ts +110 -31
  106. package/src/config/normalizers/declaration.ts +176 -0
  107. package/src/config/normalizers/pattern.ts +63 -0
  108. package/src/config/runtime.ts +310 -35
  109. package/src/declarations/helpers.ts +36 -0
  110. package/src/declarations/index.ts +19 -0
  111. package/src/declarations/types.ts +70 -0
  112. package/src/index.ts +47 -6
  113. package/src/pipeline/index.ts +1 -0
  114. package/src/pipeline/patterns.ts +10 -0
  115. package/src/transformers/README.md +2 -2
  116. package/src/transformers/docgen/index.ts +13 -2
  117. package/src/transformers/plugins/csf-render.ts +216 -49
  118. package/src/transformers/plugins/ssr-render.ts +23 -7
  119. package/src/transformers/preview/component/api.ts +27 -7
  120. package/src/transformers/preview/component/builder.ts +21 -4
  121. package/src/transformers/preview/component/html.ts +18 -5
  122. package/src/transformers/preview/component/previews.ts +12 -0
  123. package/src/transformers/preview/pattern/api.ts +66 -0
  124. package/src/transformers/preview/pattern/builder.ts +94 -0
  125. package/src/transformers/preview/pattern/html.ts +107 -0
  126. package/src/transformers/preview/types.ts +58 -1
  127. package/src/transformers/utils/csf.ts +48 -0
  128. package/src/transformers/utils/index.ts +3 -0
  129. package/src/transformers/utils/module.ts +33 -18
  130. package/src/transformers/utils/usage.ts +319 -0
  131. package/src/types/README.md +1 -1
  132. package/src/types/config.ts +33 -7
  133. package/src/types/preview.ts +2 -0
  134. package/templates/default/components/button/Button.jsx.tpl +64 -0
  135. package/templates/default/components/button/button.react.handoff.js.tpl +31 -0
  136. package/templates/default/components/button/button.react.handoff.ts.tpl +31 -0
  137. package/templates/default/handoff.config.js.tpl +29 -16
  138. package/templates/default/handoff.config.ts.tpl +196 -0
  139. package/templates/default/handoff.config.with-examples.js.tpl +28 -17
  140. package/templates/default/handoff.config.with-examples.ts.tpl +195 -0
  141. package/templates/default/package.json.tpl +1 -3
  142. package/handoff.config.js +0 -90
  143. package/templates/default/components/button/button.js.tpl +0 -62
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.syncPatternArtifacts = exports.writePatternSummaryApi = exports.writePatternHtml = exports.writePatternApi = exports.getPatternAPIPath = void 0;
16
+ const fs_extra_1 = __importDefault(require("fs-extra"));
17
+ const path_1 = __importDefault(require("path"));
18
+ const getPatternAPIPath = (handoff) => {
19
+ const patternPath = path_1.default.resolve(handoff.workingPath, 'public/api/pattern');
20
+ if (!fs_extra_1.default.existsSync(patternPath)) {
21
+ fs_extra_1.default.mkdirSync(patternPath, { recursive: true });
22
+ }
23
+ return patternPath;
24
+ };
25
+ exports.getPatternAPIPath = getPatternAPIPath;
26
+ const writePatternApi = (id, pattern, handoff) => __awaiter(void 0, void 0, void 0, function* () {
27
+ const outputDir = (0, exports.getPatternAPIPath)(handoff);
28
+ const outputFilePath = path_1.default.resolve(outputDir, `${id}.json`);
29
+ yield fs_extra_1.default.writeFile(outputFilePath, JSON.stringify(pattern, null, 2));
30
+ });
31
+ exports.writePatternApi = writePatternApi;
32
+ const writePatternHtml = (id, html, handoff) => __awaiter(void 0, void 0, void 0, function* () {
33
+ const outputDir = (0, exports.getPatternAPIPath)(handoff);
34
+ const outputFilePath = path_1.default.resolve(outputDir, `${id}.html`);
35
+ yield fs_extra_1.default.writeFile(outputFilePath, html);
36
+ });
37
+ exports.writePatternHtml = writePatternHtml;
38
+ const writePatternSummaryApi = (handoff, patterns) => __awaiter(void 0, void 0, void 0, function* () {
39
+ const apiPath = path_1.default.resolve(handoff.workingPath, 'public/api');
40
+ if (!fs_extra_1.default.existsSync(apiPath)) {
41
+ fs_extra_1.default.mkdirSync(apiPath, { recursive: true });
42
+ }
43
+ patterns.sort((a, b) => a.title.localeCompare(b.title));
44
+ yield fs_extra_1.default.writeFile(path_1.default.resolve(apiPath, 'patterns.json'), JSON.stringify(patterns, null, 2));
45
+ });
46
+ exports.writePatternSummaryApi = writePatternSummaryApi;
47
+ const syncPatternArtifacts = (handoff, validIds) => __awaiter(void 0, void 0, void 0, function* () {
48
+ const validIdSet = new Set(validIds);
49
+ const patternPath = (0, exports.getPatternAPIPath)(handoff);
50
+ const entries = yield fs_extra_1.default.readdir(patternPath);
51
+ for (const entry of entries) {
52
+ const parsed = path_1.default.parse(entry);
53
+ const isPatternArtifact = parsed.ext === '.json' || parsed.ext === '.html';
54
+ if (!isPatternArtifact)
55
+ continue;
56
+ if (!validIdSet.has(parsed.name)) {
57
+ yield fs_extra_1.default.remove(path_1.default.resolve(patternPath, entry));
58
+ }
59
+ }
60
+ });
61
+ exports.syncPatternArtifacts = syncPatternArtifacts;
@@ -0,0 +1,9 @@
1
+ import Handoff from '../../../index';
2
+ import { PatternListObject } from '../types';
3
+ /**
4
+ * Process all pattern declarations and compose their HTML from pre-built
5
+ * component preview files. This function must run AFTER buildComponents
6
+ * so that all component preview HTML files already exist on disk.
7
+ */
8
+ export declare function processPatterns(handoff: Handoff): Promise<PatternListObject[]>;
9
+ export default processPatterns;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.processPatterns = processPatterns;
16
+ const fs_extra_1 = __importDefault(require("fs-extra"));
17
+ const path_1 = __importDefault(require("path"));
18
+ const logger_1 = require("../../../utils/logger");
19
+ const api_1 = require("./api");
20
+ const html_1 = require("./html");
21
+ /**
22
+ * Process all pattern declarations and compose their HTML from pre-built
23
+ * component preview files. This function must run AFTER buildComponents
24
+ * so that all component preview HTML files already exist on disk.
25
+ */
26
+ function processPatterns(handoff) {
27
+ return __awaiter(this, void 0, void 0, function* () {
28
+ var _a, _b, _c, _d;
29
+ const runtimePatterns = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.patterns) !== null && _c !== void 0 ? _c : {};
30
+ const patternIds = Object.keys(runtimePatterns);
31
+ const result = [];
32
+ if (patternIds.length === 0) {
33
+ yield (0, api_1.syncPatternArtifacts)(handoff, []);
34
+ yield (0, api_1.writePatternSummaryApi)(handoff, result);
35
+ return result;
36
+ }
37
+ logger_1.Logger.info(`Building ${patternIds.length} pattern(s)...`);
38
+ const componentOutputDir = path_1.default.resolve(handoff.workingPath, 'public/api/component');
39
+ const basePath = (_d = process.env.HANDOFF_APP_BASE_PATH) !== null && _d !== void 0 ? _d : '';
40
+ for (const patternId of patternIds) {
41
+ const pattern = runtimePatterns[patternId];
42
+ const fragments = [];
43
+ let hasErrors = false;
44
+ for (let i = 0; i < pattern.components.length; i++) {
45
+ const ref = pattern.components[i];
46
+ if (ref.resolved === false && !ref.resolvedPreview) {
47
+ hasErrors = true;
48
+ continue;
49
+ }
50
+ const previewKey = ref.resolvedPreview || ref.preview || 'default';
51
+ const htmlFileName = `${ref.id}-${previewKey}.html`;
52
+ const htmlFilePath = path_1.default.resolve(componentOutputDir, htmlFileName);
53
+ if (!fs_extra_1.default.existsSync(htmlFilePath)) {
54
+ const error = `Pattern "${patternId}" component[${i}] ("${ref.id}") preview file not found: ${htmlFileName}. ` +
55
+ `Ensure the component has a preview named "${previewKey}".`;
56
+ logger_1.Logger.warn(error);
57
+ ref.resolved = false;
58
+ hasErrors = true;
59
+ continue;
60
+ }
61
+ ref.resolved = true;
62
+ const html = yield fs_extra_1.default.readFile(htmlFilePath, 'utf8');
63
+ fragments.push({ componentId: ref.id, html });
64
+ }
65
+ if (fragments.length === 0) {
66
+ logger_1.Logger.warn(`Pattern "${patternId}" produced no fragments. Skipping.`);
67
+ continue;
68
+ }
69
+ if (hasErrors) {
70
+ logger_1.Logger.warn(`Pattern "${patternId}" has missing components but will be composed from available fragments.`);
71
+ }
72
+ const composedHtml = (0, html_1.composePatternHtml)(patternId, pattern.title, fragments, basePath);
73
+ const patternUrl = `${patternId}.html`;
74
+ const patternData = Object.assign(Object.assign({}, pattern), { url: patternUrl, path: `${basePath}/api/pattern/${patternId}.json` });
75
+ yield (0, api_1.writePatternHtml)(patternId, composedHtml, handoff);
76
+ yield (0, api_1.writePatternApi)(patternId, patternData, handoff);
77
+ result.push(patternData);
78
+ logger_1.Logger.debug(`Built pattern: ${patternId} (${fragments.length} component(s))`);
79
+ }
80
+ yield (0, api_1.syncPatternArtifacts)(handoff, result.map((pattern) => pattern.id));
81
+ yield (0, api_1.writePatternSummaryApi)(handoff, result);
82
+ logger_1.Logger.info(`Finished building ${result.length} pattern(s).`);
83
+ return result;
84
+ });
85
+ }
86
+ exports.default = processPatterns;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Composes multiple component preview HTML documents into a single-page HTML.
3
+ *
4
+ * Each component preview may be a React SSR document (with hydration scripts
5
+ * and a `#root` / `#__APP_PROPS__` pair) or a static Handlebars document.
6
+ *
7
+ * This function namespaces the element ids per-fragment so multiple React
8
+ * components can coexist and hydrate independently on the same page.
9
+ *
10
+ * Each hydration bundle gets its own `<script type="module">` tag for full
11
+ * module-level isolation. This is critical because esbuild bundles React
12
+ * into each script using module-scoped `var` declarations, and `hydrateRoot`
13
+ * is asynchronous — sharing a single module scope would cause later bundles
14
+ * to overwrite earlier React instances before hydration completes.
15
+ */
16
+ export declare const composePatternHtml: (_patternId: string, patternTitle: string, fragments: {
17
+ componentId: string;
18
+ html: string;
19
+ }[], basePath: string) => string;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.composePatternHtml = void 0;
7
+ const escape_1 = __importDefault(require("lodash/escape"));
8
+ const node_html_parser_1 = require("node-html-parser");
9
+ /**
10
+ * Composes multiple component preview HTML documents into a single-page HTML.
11
+ *
12
+ * Each component preview may be a React SSR document (with hydration scripts
13
+ * and a `#root` / `#__APP_PROPS__` pair) or a static Handlebars document.
14
+ *
15
+ * This function namespaces the element ids per-fragment so multiple React
16
+ * components can coexist and hydrate independently on the same page.
17
+ *
18
+ * Each hydration bundle gets its own `<script type="module">` tag for full
19
+ * module-level isolation. This is critical because esbuild bundles React
20
+ * into each script using module-scoped `var` declarations, and `hydrateRoot`
21
+ * is asynchronous — sharing a single module scope would cause later bundles
22
+ * to overwrite earlier React instances before hydration completes.
23
+ */
24
+ const composePatternHtml = (_patternId, patternTitle, fragments, basePath) => {
25
+ const cssHrefs = new Set();
26
+ const bodyParts = [];
27
+ const scriptTags = [];
28
+ cssHrefs.add(`${basePath}/api/component/main.css`);
29
+ cssHrefs.add(`${basePath}/assets/css/preview.css`);
30
+ const seenComponents = new Set();
31
+ for (let i = 0; i < fragments.length; i++) {
32
+ const { componentId, html } = fragments[i];
33
+ if (!seenComponents.has(componentId)) {
34
+ seenComponents.add(componentId);
35
+ cssHrefs.add(`${basePath}/api/component/${componentId}.css`);
36
+ }
37
+ const doc = (0, node_html_parser_1.parse)(html);
38
+ const suffix = `_p${i}`;
39
+ const namespacedRootId = `root${suffix}`;
40
+ const namespacedPropsId = `__APP_PROPS__${suffix}`;
41
+ // -- Extract props JSON (React SSR) ------------------------------------
42
+ const propsScript = doc.querySelector('#__APP_PROPS__');
43
+ let propsContent = '{}';
44
+ if (propsScript) {
45
+ propsContent = propsScript.textContent || '{}';
46
+ propsScript.remove();
47
+ }
48
+ // -- Extract and rewrite hydration scripts (React SSR) -----------------
49
+ // Each script becomes its own <script type="module"> for full isolation.
50
+ const moduleScripts = doc.querySelectorAll('script[type="module"]');
51
+ for (const script of moduleScripts) {
52
+ let code = script.textContent || '';
53
+ if (code.trim()) {
54
+ code = code
55
+ .replace(/__APP_PROPS__/g, namespacedPropsId)
56
+ .replace(/getElementById\(\s*["']root["']\s*\)/g, `getElementById("${namespacedRootId}")`);
57
+ scriptTags.push(` <script type="module">\n${code}\n </script>`);
58
+ }
59
+ script.remove();
60
+ }
61
+ // -- Namespace the root element (React SSR) ----------------------------
62
+ const rootEl = doc.querySelector('#root');
63
+ if (rootEl) {
64
+ rootEl.setAttribute('id', namespacedRootId);
65
+ }
66
+ // -- Extract body content ----------------------------------------------
67
+ const bodyEl = doc.querySelector('body');
68
+ const bodyContent = bodyEl
69
+ ? bodyEl.innerHTML.trim()
70
+ : rootEl
71
+ ? rootEl.outerHTML
72
+ : doc.innerHTML || '';
73
+ bodyParts.push(` <div class="handoff-pattern-block" data-component="${componentId}" data-fragment="${i}">\n` +
74
+ ` <script id="${namespacedPropsId}" type="application/json">${propsContent}</script>\n` +
75
+ ` ${bodyContent}\n` +
76
+ ` </div>`);
77
+ }
78
+ const linkTags = Array.from(cssHrefs)
79
+ .map((href) => ` <link rel="stylesheet" href="${href}" />`)
80
+ .join('\n');
81
+ return `<!DOCTYPE html>
82
+ <html lang="en">
83
+ <head>
84
+ <meta charset="UTF-8" />
85
+ ${linkTags}
86
+ <title>${(0, escape_1.default)(patternTitle)}</title>
87
+ </head>
88
+ <body>
89
+ ${bodyParts.join('\n')}
90
+ ${scriptTags.join('\n')}
91
+ </body>
92
+ </html>`;
93
+ };
94
+ exports.composePatternHtml = composePatternHtml;
@@ -1,5 +1,6 @@
1
1
  import type { GeneratedDocs } from 'handoff-docgen';
2
2
  import { Card } from '../../app/components/Component/Cards';
3
+ import type { RendererKind } from '../../declarations/types';
3
4
  import { ValidationResult } from '../../types/preview';
4
5
  import { Filter } from '../../utils/filter';
5
6
  import { SlotMetadata } from './component';
@@ -93,7 +94,17 @@ export type ComponentObject = {
93
94
  scss?: string;
94
95
  /** Optional path to component template file (if available) */
95
96
  template?: string;
97
+ /** Optional path to React component source for preview rendering */
98
+ component?: string;
99
+ /** Optional path to CSF story file */
100
+ story?: string;
101
+ /** Optional path to schema file (if available) */
102
+ schema?: string;
103
+ /** Optional path to templates directory */
104
+ templates?: string;
96
105
  };
106
+ /** Optional explicit renderer id */
107
+ renderer?: RendererKind;
97
108
  /** Schema describing the expected properties (props/slots) for the component */
98
109
  properties: {
99
110
  [key: string]: SlotMetadata;
@@ -102,10 +113,18 @@ export type ComponentObject = {
102
113
  previews: {
103
114
  [key: string]: OptionalPreviewRender;
104
115
  };
116
+ /** Internal previews generated only for pattern composition, not public docs */
117
+ internalPatternPreviews?: {
118
+ [key: string]: OptionalPreviewRender;
119
+ };
105
120
  /** Optional array of high-level categories for search/filtering */
106
121
  categories?: string[];
107
122
  /** Optional array of tags for search/filtering (e.g. "primary", "interactive") */
108
123
  tags?: string[];
124
+ /** Optional markdown strings describing recommended usage patterns */
125
+ should_do?: string[];
126
+ /** Optional markdown strings describing anti-patterns to avoid */
127
+ should_not_do?: string[];
109
128
  /** Optional source Figma file or node URL for the component */
110
129
  figma?: string;
111
130
  /** Optional canonical Figma component name or ID (used for matching back to design tokens) */
@@ -138,11 +157,15 @@ export type TransformComponentTokensResult = {
138
157
  css?: string;
139
158
  sass?: string;
140
159
  sharedStyles?: string;
160
+ usage?: string;
141
161
  title?: string;
142
162
  description?: string;
143
163
  previews?: {
144
164
  [key: string]: OptionalPreviewRender;
145
165
  };
166
+ internalPatternPreviews?: {
167
+ [key: string]: OptionalPreviewRender;
168
+ };
146
169
  properties?: {
147
170
  [key: string]: SlotMetadata;
148
171
  };
@@ -151,8 +174,12 @@ export type TransformComponentTokensResult = {
151
174
  js?: string;
152
175
  scss?: string;
153
176
  template?: string;
177
+ component?: string;
178
+ story?: string;
154
179
  schema?: string;
180
+ templates?: string;
155
181
  };
182
+ renderer?: RendererKind;
156
183
  options?: {
157
184
  preview?: {
158
185
  groupBy?: string;
@@ -168,7 +195,34 @@ export type OptionalPreviewRender = {
168
195
  [key: string]: string | string[] | any;
169
196
  };
170
197
  url: string;
198
+ usage?: string;
199
+ /** Optional source preview/story key used to derive this preview */
200
+ sourcePreview?: string;
171
201
  };
172
202
  export interface TransformedPreviewComponents {
173
203
  [key: string]: TransformComponentTokensResult[];
174
204
  }
205
+ export type PatternComponentEntry = {
206
+ /** Component id reference */
207
+ id: string;
208
+ /** Named preview variant to use (optional if args provided) */
209
+ preview?: string;
210
+ /** Arg overrides (optional if preview provided) */
211
+ args?: Record<string, any>;
212
+ /** Preview key resolved during injectPatternPreviews (set at build time) */
213
+ resolvedPreview?: string;
214
+ /** Whether the pattern entry resolved to a usable component preview */
215
+ resolved?: boolean;
216
+ };
217
+ export type PatternObject = {
218
+ title: string;
219
+ description?: string;
220
+ group?: string;
221
+ tags?: string[];
222
+ components: PatternComponentEntry[];
223
+ url?: string;
224
+ };
225
+ export type PatternListObject = {
226
+ id: string;
227
+ path: string;
228
+ } & PatternObject;
@@ -0,0 +1,16 @@
1
+ import { OptionalPreviewRender } from '../preview/types';
2
+ export type StoryObject = {
3
+ name?: string;
4
+ args?: Record<string, any>;
5
+ argTypes?: Record<string, any>;
6
+ render?: (args: Record<string, any>) => any;
7
+ };
8
+ export type CsfMeta = {
9
+ title?: string;
10
+ component?: any;
11
+ args?: Record<string, any>;
12
+ argTypes?: Record<string, any>;
13
+ render?: (args: Record<string, any>) => any;
14
+ };
15
+ export declare function getCsfStoryEntries(moduleExports: Record<string, any>): Array<[string, StoryObject]>;
16
+ export declare function createCsfStoryPreviews(moduleExports: Record<string, any>): Record<string, OptionalPreviewRender>;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCsfStoryEntries = getCsfStoryEntries;
4
+ exports.createCsfStoryPreviews = createCsfStoryPreviews;
5
+ const lodash_1 = require("lodash");
6
+ function getCsfStoryEntries(moduleExports) {
7
+ return Object.entries(moduleExports).filter(([key, value]) => {
8
+ if (key === 'default' || key === '__esModule')
9
+ return false;
10
+ if (typeof value === 'function')
11
+ return true;
12
+ if (value && typeof value === 'object')
13
+ return true;
14
+ return false;
15
+ });
16
+ }
17
+ function createCsfStoryPreviews(moduleExports) {
18
+ const meta = (moduleExports.default || {});
19
+ const stories = getCsfStoryEntries(moduleExports);
20
+ const previews = {};
21
+ for (const [storyKey, storyValue] of stories) {
22
+ const storyObj = (typeof storyValue === 'function' ? {} : storyValue);
23
+ previews[storyKey] = {
24
+ title: storyObj.name || (0, lodash_1.startCase)(storyKey),
25
+ values: Object.assign(Object.assign({}, (meta.args || {})), (storyObj.args || {})),
26
+ url: '',
27
+ };
28
+ }
29
+ return previews;
30
+ }
@@ -8,5 +8,6 @@ export * from './schema';
8
8
  export * from './html';
9
9
  export * from './build';
10
10
  export * from './module';
11
+ export * from './csf';
11
12
  export * from './handlebars';
12
13
  export * from './schema-loader';
@@ -28,6 +28,8 @@ __exportStar(require("./html"), exports);
28
28
  __exportStar(require("./build"), exports);
29
29
  // Module utilities
30
30
  __exportStar(require("./module"), exports);
31
+ // CSF utilities
32
+ __exportStar(require("./csf"), exports);
31
33
  // Handlebars utilities
32
34
  __exportStar(require("./handlebars"), exports);
33
35
  // Schema loader utilities
@@ -6,3 +6,8 @@ import { ModuleEvaluationResult } from '../types';
6
6
  * @returns Module evaluation result with exports
7
7
  */
8
8
  export declare function buildAndEvaluateModule(entryPath: string, handoff: any): Promise<ModuleEvaluationResult>;
9
+ /**
10
+ * Builds and evaluates a module synchronously using esbuild.
11
+ * Used by config/runtime code paths that must remain synchronous.
12
+ */
13
+ export declare function buildAndEvaluateModuleSync(entryPath: string, handoff: any): ModuleEvaluationResult;
@@ -13,8 +13,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.buildAndEvaluateModule = buildAndEvaluateModule;
16
+ exports.buildAndEvaluateModuleSync = buildAndEvaluateModuleSync;
16
17
  const esbuild_1 = __importDefault(require("esbuild"));
17
18
  const build_1 = require("./build");
19
+ function createModuleBuildConfig(entryPath, handoff) {
20
+ var _a, _b;
21
+ const defaultBuildConfig = Object.assign(Object.assign({}, build_1.DEFAULT_SSR_BUILD_CONFIG), { entryPoints: [entryPath] });
22
+ return ((_b = (_a = handoff.config) === null || _a === void 0 ? void 0 : _a.hooks) === null || _b === void 0 ? void 0 : _b.ssrBuildConfig)
23
+ ? handoff.config.hooks.ssrBuildConfig(defaultBuildConfig)
24
+ : defaultBuildConfig;
25
+ }
26
+ function evaluateBuiltModule(code) {
27
+ const mod = { exports: {} };
28
+ const func = new Function('require', 'module', 'exports', code);
29
+ func(require, mod, mod.exports);
30
+ return mod;
31
+ }
18
32
  /**
19
33
  * Builds and evaluates a module using esbuild
20
34
  * @param entryPath - Path to the module entry point
@@ -23,20 +37,19 @@ const build_1 = require("./build");
23
37
  */
24
38
  function buildAndEvaluateModule(entryPath, handoff) {
25
39
  return __awaiter(this, void 0, void 0, function* () {
26
- var _a, _b;
27
- // Default esbuild configuration
28
- const defaultBuildConfig = Object.assign(Object.assign({}, build_1.DEFAULT_SSR_BUILD_CONFIG), { entryPoints: [entryPath] });
29
- // Apply user's SSR build config hook if provided
30
- const buildConfig = ((_b = (_a = handoff.config) === null || _a === void 0 ? void 0 : _a.hooks) === null || _b === void 0 ? void 0 : _b.ssrBuildConfig)
31
- ? handoff.config.hooks.ssrBuildConfig(defaultBuildConfig)
32
- : defaultBuildConfig;
33
- // Compile the module
40
+ const buildConfig = createModuleBuildConfig(entryPath, handoff);
34
41
  const build = yield esbuild_1.default.build(buildConfig);
35
42
  const { text: code } = build.outputFiles[0];
36
- // Evaluate the compiled code
37
- const mod = { exports: {} };
38
- const func = new Function('require', 'module', 'exports', code);
39
- func(require, mod, mod.exports);
40
- return mod;
43
+ return evaluateBuiltModule(code);
41
44
  });
42
45
  }
46
+ /**
47
+ * Builds and evaluates a module synchronously using esbuild.
48
+ * Used by config/runtime code paths that must remain synchronous.
49
+ */
50
+ function buildAndEvaluateModuleSync(entryPath, handoff) {
51
+ const buildConfig = createModuleBuildConfig(entryPath, handoff);
52
+ const build = esbuild_1.default.buildSync(buildConfig);
53
+ const { text: code } = build.outputFiles[0];
54
+ return evaluateBuiltModule(code);
55
+ }
@@ -0,0 +1,23 @@
1
+ import { SlotMetadata } from '../preview/component';
2
+ /**
3
+ * Extracts the PascalCase component name from a file path.
4
+ * e.g. `/path/to/Button.tsx` -> `Button`
5
+ */
6
+ export declare function extractComponentName(filePath: string): string;
7
+ export interface GenerateUsageSnippetOptions {
8
+ componentName: string;
9
+ properties: Record<string, SlotMetadata>;
10
+ previewValues: Record<string, any>;
11
+ /** Template filename for the import path (e.g. "Button.tsx") */
12
+ templateFileName?: string;
13
+ importStatement?: string;
14
+ }
15
+ /**
16
+ * Generates a JSX usage snippet showing how to consume a React component
17
+ * with the given preview values.
18
+ *
19
+ * Iterates over the union of preview values and schema properties so that
20
+ * props not present in the preview are still represented when they carry
21
+ * a meaningful default or are required.
22
+ */
23
+ export declare function generateUsageSnippet(options: GenerateUsageSnippetOptions): string;