handoff-app 1.1.0-experimental.2 → 1.1.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.
Files changed (174) hide show
  1. package/Changelog.md +6 -3
  2. package/config/docs/system.md +0 -2
  3. package/dist/app/components/Component/Cards.jsx +32 -0
  4. package/dist/app-builder/build.d.ts +1 -6
  5. package/dist/app-builder/build.js +34 -54
  6. package/dist/app-builder/index.d.ts +0 -1
  7. package/dist/app-builder/index.js +1 -9
  8. package/dist/app-builder/watchers.d.ts +1 -5
  9. package/dist/app-builder/watchers.js +9 -68
  10. package/dist/cache/build-cache.js +25 -10
  11. package/dist/cli.js +1 -1
  12. package/dist/commands/index.js +2 -16
  13. package/dist/config/runtime.d.ts +0 -5
  14. package/dist/config/runtime.js +4 -67
  15. package/dist/index.d.ts +1 -4
  16. package/dist/index.js +2 -2
  17. package/dist/pipeline/index.d.ts +0 -1
  18. package/dist/pipeline/index.js +1 -3
  19. package/dist/transformers/plugins/csf-render.js +1 -18
  20. package/dist/transformers/plugins/handlebars-previews.js +12 -1
  21. package/dist/transformers/plugins/ssr-render.js +0 -14
  22. package/dist/transformers/preview/component/builder.js +16 -1
  23. package/dist/transformers/utils/build.d.ts +0 -18
  24. package/dist/transformers/utils/build.js +0 -108
  25. package/dist/transformers/utils/html.js +15 -3
  26. package/dist/types/config.d.ts +0 -19
  27. package/dist/utils/path.d.ts +15 -0
  28. package/dist/utils/path.js +31 -0
  29. package/docs/api.md +279 -0
  30. package/docs/api_spec.yml +0 -0
  31. package/docs/cli.md +52 -66
  32. package/docs/props.md +34 -0
  33. package/package.json +6 -10
  34. package/src/app/components/Component/ComponentLists.tsx +5 -4
  35. package/src/app/components/Navigation/MainNav.tsx +3 -5
  36. package/src/app/components/Navigation/SideNav.tsx +4 -7
  37. package/src/app/components/SideNav/Custom.tsx +5 -3
  38. package/src/app/components/ui/mobile-nav.tsx +10 -7
  39. package/src/app/components/util/index.ts +11 -64
  40. package/src/app/lib/utils.ts +13 -0
  41. package/src/app/next.config.mjs +31 -15
  42. package/src/app/pages/system/component/[component]/index.tsx +15 -8
  43. package/src/app/pages/system/component/index.tsx +10 -87
  44. package/src/app/pages/system/index.tsx +5 -24
  45. package/src/app/pages/system/tokens/components/[component]/index.tsx +14 -1
  46. package/src/app/pages/system/tokens/index.tsx +10 -85
  47. package/src/app/tsconfig.json +1 -1
  48. package/src/app-builder/README.md +1 -1
  49. package/src/app-builder/build.ts +37 -70
  50. package/src/app-builder/index.ts +0 -9
  51. package/src/app-builder/watchers.ts +9 -67
  52. package/src/cache/README.md +2 -2
  53. package/src/cache/build-cache.ts +29 -11
  54. package/src/cli.ts +1 -1
  55. package/src/commands/index.ts +0 -14
  56. package/src/config/runtime.ts +0 -76
  57. package/src/index.ts +3 -3
  58. package/src/pipeline/README.md +1 -0
  59. package/src/pipeline/index.ts +0 -1
  60. package/src/transformers/README.md +3 -2
  61. package/src/transformers/plugins/csf-render.ts +0 -17
  62. package/src/transformers/plugins/handlebars-previews.ts +13 -1
  63. package/src/transformers/plugins/ssr-render.ts +8 -17
  64. package/src/transformers/preview/component/builder.ts +17 -1
  65. package/src/transformers/utils/build.ts +0 -112
  66. package/src/transformers/utils/html.ts +16 -3
  67. package/src/types/README.md +2 -1
  68. package/src/types/config.ts +0 -21
  69. package/src/utils/path.ts +29 -0
  70. package/tsconfig.json +1 -1
  71. package/config/docs/playground.md +0 -14
  72. package/config/docs/system/alert.md +0 -30
  73. package/config/docs/system/button.md +0 -34
  74. package/config/docs/system/checkbox.md +0 -30
  75. package/config/docs/system/input.md +0 -31
  76. package/config/docs/system/modal.md +0 -32
  77. package/config/docs/system/pagination.md +0 -30
  78. package/config/docs/system/radio.md +0 -30
  79. package/config/docs/system/select.md +0 -30
  80. package/config/docs/system/switch.md +0 -30
  81. package/config/docs/system/tooltip.md +0 -30
  82. package/dist/app/components/Component/Cards.js +0 -17
  83. package/dist/app-builder/isr-paths.d.ts +0 -44
  84. package/dist/app-builder/isr-paths.js +0 -213
  85. package/dist/commands/isr/component.d.ts +0 -10
  86. package/dist/commands/isr/component.js +0 -63
  87. package/dist/commands/isr/pages.d.ts +0 -11
  88. package/dist/commands/isr/pages.js +0 -64
  89. package/dist/commands/platform/init.d.ts +0 -6
  90. package/dist/commands/platform/init.js +0 -231
  91. package/dist/commands/platform/login.d.ts +0 -6
  92. package/dist/commands/platform/login.js +0 -108
  93. package/dist/commands/platform/logout.d.ts +0 -6
  94. package/dist/commands/platform/logout.js +0 -88
  95. package/dist/commands/platform/pull.d.ts +0 -8
  96. package/dist/commands/platform/pull.js +0 -112
  97. package/dist/commands/platform/push.d.ts +0 -8
  98. package/dist/commands/platform/push.js +0 -428
  99. package/dist/pipeline/pages.d.ts +0 -6
  100. package/dist/pipeline/pages.js +0 -21
  101. package/dist/platform/client.d.ts +0 -122
  102. package/dist/platform/client.js +0 -228
  103. package/dist/platform/credentials.d.ts +0 -40
  104. package/dist/platform/credentials.js +0 -91
  105. package/dist/platform/sync.d.ts +0 -68
  106. package/dist/platform/sync.js +0 -349
  107. package/dist/transformers/preview/page/api.d.ts +0 -8
  108. package/dist/transformers/preview/page/api.js +0 -74
  109. package/dist/transformers/preview/page/builder.d.ts +0 -7
  110. package/dist/transformers/preview/page/builder.js +0 -173
  111. package/dist/transformers/preview/page/types.d.ts +0 -38
  112. package/dist/transformers/preview/page/types.js +0 -2
  113. package/docs/components.md +0 -107
  114. package/docs/isr.md +0 -113
  115. package/docs/pages.md +0 -152
  116. package/docs/rest.yml +0 -590
  117. package/docs/rest_api.md +0 -78
  118. package/eslint.config.mjs +0 -18
  119. package/handoff.config.js +0 -90
  120. package/src/app/components/Page/PageLists.tsx +0 -218
  121. package/src/app/components/Page/types.ts +0 -19
  122. package/src/app/components/Playground/ComponentCard.tsx +0 -78
  123. package/src/app/components/Playground/ComponentLibrary.tsx +0 -99
  124. package/src/app/components/Playground/EditContext.tsx +0 -159
  125. package/src/app/components/Playground/EditSheet.tsx +0 -80
  126. package/src/app/components/Playground/MediaBrowser.tsx +0 -56
  127. package/src/app/components/Playground/PlaygroundBuilder.tsx +0 -160
  128. package/src/app/components/Playground/PlaygroundContext.tsx +0 -276
  129. package/src/app/components/Playground/Preview.tsx +0 -174
  130. package/src/app/components/Playground/SortableItem.tsx +0 -45
  131. package/src/app/components/Playground/TemplateManager.tsx +0 -81
  132. package/src/app/components/Playground/Wizard/ApiKeySettings.tsx +0 -125
  133. package/src/app/components/Playground/Wizard/WizardDialog.tsx +0 -228
  134. package/src/app/components/Playground/Wizard/llm-client.ts +0 -87
  135. package/src/app/components/Playground/Wizard/prompt-builder.ts +0 -78
  136. package/src/app/components/Playground/Wizard/response-parser.ts +0 -81
  137. package/src/app/components/Playground/fields/ButtonField.tsx +0 -24
  138. package/src/app/components/Playground/fields/Field.tsx +0 -112
  139. package/src/app/components/Playground/fields/FieldLabel.tsx +0 -20
  140. package/src/app/components/Playground/fields/ImageField.tsx +0 -92
  141. package/src/app/components/Playground/fields/LinkField.tsx +0 -24
  142. package/src/app/components/Playground/fields/RichTextField.tsx +0 -34
  143. package/src/app/components/Playground/fields/TextField.tsx +0 -13
  144. package/src/app/components/Playground/types.ts +0 -30
  145. package/src/app/components/ui/dialog.tsx +0 -80
  146. package/src/app/components/ui/textarea.tsx +0 -19
  147. package/src/app/pages/playground/index.tsx +0 -30
  148. package/src/app/pages/system/page/[page]/index.tsx +0 -300
  149. package/src/app/pages/system/page/index.tsx +0 -124
  150. package/src/app-builder/isr-paths.ts +0 -209
  151. package/src/commands/isr/component.ts +0 -68
  152. package/src/commands/isr/pages.ts +0 -68
  153. package/src/commands/platform/init.ts +0 -229
  154. package/src/commands/platform/login.ts +0 -96
  155. package/src/commands/platform/logout.ts +0 -63
  156. package/src/commands/platform/pull.ts +0 -98
  157. package/src/commands/platform/push.ts +0 -469
  158. package/src/pipeline/pages.ts +0 -10
  159. package/src/platform/client.ts +0 -335
  160. package/src/platform/credentials.ts +0 -106
  161. package/src/platform/sync.ts +0 -428
  162. package/src/transformers/preview/page/api.ts +0 -65
  163. package/src/transformers/preview/page/builder.ts +0 -200
  164. package/src/transformers/preview/page/types.ts +0 -37
  165. package/test_components/example/example.js +0 -110
  166. package/test_components/example/style.scss +0 -56
  167. package/test_components/example/template.hbs +0 -19
  168. package/test_components/example-csf/example-csf.js +0 -16
  169. package/test_components/example-csf/style.scss +0 -52
  170. package/test_components/example-csf/template.stories.tsx +0 -97
  171. package/test_components/example-react/example-react.js +0 -69
  172. package/test_components/example-react/style.scss +0 -56
  173. package/test_components/example-react/template.tsx +0 -49
  174. package/test_pages/boilerplate/boilerplate.json +0 -73
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.toLowerCaseKeysAndValues = exports.getPagesForPath = exports.getComponentsForPath = exports.initRuntimeConfig = void 0;
6
+ exports.toLowerCaseKeysAndValues = exports.getComponentsForPath = exports.initRuntimeConfig = void 0;
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const logger_1 = require("../utils/logger");
@@ -15,8 +15,8 @@ const logger_1 = require("../utils/logger");
15
15
  * @returns A tuple of [RuntimeConfig, configFilePaths].
16
16
  */
17
17
  const initRuntimeConfig = (handoff) => {
18
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
19
- var _l;
18
+ var _a, _b, _c, _d, _e, _f, _g, _h;
19
+ var _j;
20
20
  const configFiles = [];
21
21
  const result = {
22
22
  options: {},
@@ -24,7 +24,6 @@ const initRuntimeConfig = (handoff) => {
24
24
  scss: undefined,
25
25
  js: undefined,
26
26
  components: {},
27
- pages: {},
28
27
  },
29
28
  };
30
29
  if (!!((_a = handoff.config.entries) === null || _a === void 0 ? void 0 : _a.scss)) {
@@ -77,7 +76,7 @@ const initRuntimeConfig = (handoff) => {
77
76
  component.options || (component.options = {
78
77
  transformer: { defaults: {}, replace: {} },
79
78
  });
80
- (_l = component.options).transformer || (_l.transformer = { defaults: {}, replace: {} });
79
+ (_j = component.options).transformer || (_j.transformer = { defaults: {}, replace: {} });
81
80
  const transformer = component.options.transformer;
82
81
  (_g = transformer.cssRootClass) !== null && _g !== void 0 ? _g : (transformer.cssRootClass = null);
83
82
  (_h = transformer.tokenNameSegments) !== null && _h !== void 0 ? _h : (transformer.tokenNameSegments = null);
@@ -90,47 +89,6 @@ const initRuntimeConfig = (handoff) => {
90
89
  result.entries.components[component.id] = component;
91
90
  }
92
91
  }
93
- if ((_k = (_j = handoff.config.entries) === null || _j === void 0 ? void 0 : _j.pages) === null || _k === void 0 ? void 0 : _k.length) {
94
- const pagePaths = handoff.config.entries.pages.flatMap(exports.getPagesForPath);
95
- for (const pagePath of pagePaths) {
96
- const resolvedPagePath = path_1.default.resolve(handoff.workingPath, pagePath);
97
- const pageBaseName = path_1.default.basename(resolvedPagePath);
98
- const possibleConfigFiles = [`${pageBaseName}.json`, `${pageBaseName}.js`, `${pageBaseName}.cjs`];
99
- const configFileName = possibleConfigFiles.find((file) => fs_extra_1.default.existsSync(path_1.default.resolve(resolvedPagePath, file)));
100
- if (!configFileName) {
101
- logger_1.Logger.warn(`Missing page config: ${path_1.default.resolve(resolvedPagePath, possibleConfigFiles.join(' or '))}`);
102
- continue;
103
- }
104
- const resolvedPageConfigPath = path_1.default.resolve(resolvedPagePath, configFileName);
105
- configFiles.push(resolvedPageConfigPath);
106
- let page;
107
- try {
108
- if (configFileName.endsWith('.json')) {
109
- const pageJson = fs_extra_1.default.readFileSync(resolvedPageConfigPath, 'utf8');
110
- page = JSON.parse(pageJson);
111
- }
112
- else {
113
- delete require.cache[require.resolve(resolvedPageConfigPath)];
114
- const importedPage = require(resolvedPageConfigPath);
115
- page = importedPage.default || importedPage;
116
- }
117
- }
118
- catch (err) {
119
- logger_1.Logger.error(`Failed to read or parse page config: ${resolvedPageConfigPath}`, err);
120
- continue;
121
- }
122
- page.id = pageBaseName;
123
- // Validate that referenced components exist
124
- if (page.components) {
125
- for (const componentId of page.components) {
126
- if (!result.entries.components[componentId]) {
127
- logger_1.Logger.warn(`Page '${page.id}' references unknown component '${componentId}'`);
128
- }
129
- }
130
- }
131
- result.entries.pages[page.id] = page;
132
- }
133
- }
134
92
  return [result, Array.from(configFiles)];
135
93
  };
136
94
  exports.initRuntimeConfig = initRuntimeConfig;
@@ -170,27 +128,6 @@ const getComponentsForPath = (searchPath) => {
170
128
  return [searchPath];
171
129
  };
172
130
  exports.getComponentsForPath = getComponentsForPath;
173
- /**
174
- * Returns a list of page directories for a given path.
175
- * Mirrors getComponentsForPath but for page definitions.
176
- */
177
- const getPagesForPath = (searchPath) => {
178
- const dirName = path_1.default.basename(searchPath);
179
- const possibleConfigFiles = [`${dirName}.json`, `${dirName}.js`, `${dirName}.cjs`];
180
- const hasOwnConfig = possibleConfigFiles.some((file) => fs_extra_1.default.existsSync(path_1.default.resolve(searchPath, file)));
181
- if (hasOwnConfig) {
182
- return [searchPath];
183
- }
184
- const subdirectories = fs_extra_1.default
185
- .readdirSync(searchPath, { withFileTypes: true })
186
- .filter((entry) => entry.isDirectory())
187
- .map((entry) => entry.name);
188
- if (subdirectories.length > 0) {
189
- return subdirectories.map((subdir) => path_1.default.join(searchPath, subdir));
190
- }
191
- return [searchPath];
192
- };
193
- exports.getPagesForPath = getPagesForPath;
194
131
  /**
195
132
  * Recursively converts all keys and string values in an object to lowercase.
196
133
  */
package/dist/index.d.ts CHANGED
@@ -26,10 +26,7 @@ declare class Handoff {
26
26
  preRunner(validate?: boolean): Handoff;
27
27
  fetch(): Promise<Handoff>;
28
28
  component(name: string | null): Promise<Handoff>;
29
- build(skipComponents?: boolean, options?: {
30
- copyOnlyPaths?: string[];
31
- deployDir?: string;
32
- }): Promise<Handoff>;
29
+ build(skipComponents?: boolean): Promise<Handoff>;
33
30
  ejectConfig(): Promise<Handoff>;
34
31
  ejectPages(): Promise<Handoff>;
35
32
  ejectTheme(): Promise<Handoff>;
package/dist/index.js CHANGED
@@ -114,10 +114,10 @@ class Handoff {
114
114
  return this;
115
115
  });
116
116
  }
117
- build(skipComponents, options) {
117
+ build(skipComponents) {
118
118
  return __awaiter(this, void 0, void 0, function* () {
119
119
  this.preRunner();
120
- yield (0, app_builder_1.default)(this, skipComponents, options);
120
+ yield (0, app_builder_1.default)(this, skipComponents);
121
121
  return this;
122
122
  });
123
123
  }
@@ -2,7 +2,6 @@ import 'dotenv/config';
2
2
  import Handoff from '..';
3
3
  export { readPrevJSONFile, zip, zipAssets } from './archive';
4
4
  export { buildComponents } from './components';
5
- export { buildPages } from './pages';
6
5
  /**
7
6
  * Run the entire Figma data pipeline:
8
7
  * 1. Validate runtime requirements
@@ -12,7 +12,7 @@ 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.buildPages = exports.buildComponents = exports.zipAssets = exports.zip = exports.readPrevJSONFile = void 0;
15
+ exports.buildComponents = exports.zipAssets = exports.zip = exports.readPrevJSONFile = void 0;
16
16
  require("dotenv/config");
17
17
  const app_builder_1 = __importDefault(require("../app-builder"));
18
18
  const logger_1 = require("../utils/logger");
@@ -26,8 +26,6 @@ Object.defineProperty(exports, "zip", { enumerable: true, get: function () { ret
26
26
  Object.defineProperty(exports, "zipAssets", { enumerable: true, get: function () { return archive_1.zipAssets; } });
27
27
  var components_1 = require("./components");
28
28
  Object.defineProperty(exports, "buildComponents", { enumerable: true, get: function () { return components_1.buildComponents; } });
29
- var pages_1 = require("./pages");
30
- Object.defineProperty(exports, "buildPages", { enumerable: true, get: function () { return pages_1.buildPages; } });
31
29
  /**
32
30
  * Run the entire Figma data pipeline:
33
31
  * 1. Validate runtime requirements
@@ -20,7 +20,6 @@ const react_1 = __importDefault(require("react"));
20
20
  const server_1 = __importDefault(require("react-dom/server"));
21
21
  const logger_1 = require("../../utils/logger");
22
22
  const component_1 = require("../preview/component");
23
- const build_1 = require("../utils/build");
24
23
  const html_1 = require("../utils/html");
25
24
  const module_1 = require("../utils/module");
26
25
  const schema_1 = require("../utils/schema");
@@ -154,7 +153,7 @@ function csfRenderPlugin(componentData, _documentationComponents, handoff) {
154
153
  },
155
154
  generateBundle(_, bundle) {
156
155
  return __awaiter(this, void 0, void 0, function* () {
157
- var _a, _b, _c, _d, _e, _f;
156
+ var _a, _b, _c, _d, _e;
158
157
  for (const [fileName, chunkInfo] of Object.entries(bundle)) {
159
158
  if (chunkInfo.type === 'chunk' && fileName.includes(PLUGIN_CONSTANTS.SCRIPT_ID)) {
160
159
  delete bundle[fileName];
@@ -261,22 +260,6 @@ function csfRenderPlugin(componentData, _documentationComponents, handoff) {
261
260
  preview.url = fileName;
262
261
  lastHtml = html;
263
262
  }
264
- // Emit standalone client module for Playground live rendering
265
- const firstStoryKey = (_f = stories[0]) === null || _f === void 0 ? void 0 : _f[0];
266
- if (firstStoryKey) {
267
- try {
268
- const clientModuleSource = (0, build_1.generateCsfClientComponentSource)(templatePath, firstStoryKey);
269
- const clientModuleJs = yield (0, build_1.buildClientModule)(clientModuleSource, handoff);
270
- this.emitFile({
271
- type: 'asset',
272
- fileName: `${componentId}.module.js`,
273
- source: clientModuleJs,
274
- });
275
- }
276
- catch (error) {
277
- logger_1.Logger.warn(`Failed to build client module for ${componentId}: ${error}`);
278
- }
279
- }
280
263
  componentData.format = 'react';
281
264
  componentData.preview = '';
282
265
  componentData.code = (0, html_1.trimPreview)(sourceCode);
@@ -131,6 +131,16 @@ function handlebarsPreviewsPlugin(componentData, documentationComponents, handof
131
131
  }
132
132
  // Process component instances from documentation
133
133
  processComponentInstances(componentData, documentationComponents);
134
+ if (!componentData.previews || Object.keys(componentData.previews).length === 0) {
135
+ logger_1.Logger.warn(`No previews defined for ${componentId}; using default preview values.`);
136
+ componentData.previews = {
137
+ default: {
138
+ title: 'Default',
139
+ values: {},
140
+ url: '',
141
+ },
142
+ };
143
+ }
134
144
  const generatedPreviews = {};
135
145
  // Generate previews for each variation
136
146
  for (const previewKey in componentData.previews) {
@@ -153,7 +163,8 @@ function handlebarsPreviewsPlugin(componentData, documentationComponents, handof
153
163
  componentData.format = PLUGIN_CONSTANTS.OUTPUT_FORMAT;
154
164
  componentData.preview = '';
155
165
  componentData.code = (0, html_1.trimPreview)(templateContent);
156
- componentData.html = (0, html_1.trimPreview)(generatedPreviews[Object.keys(generatedPreviews)[0]]);
166
+ const firstGeneratedPreview = Object.values(generatedPreviews)[0];
167
+ componentData.html = (0, html_1.trimPreview)(firstGeneratedPreview !== null && firstGeneratedPreview !== void 0 ? firstGeneratedPreview : templateContent);
157
168
  });
158
169
  },
159
170
  };
@@ -257,20 +257,6 @@ function ssrRenderPlugin(componentData, documentationComponents, handoff) {
257
257
  generatedPreviews[previewKey] = finalHtml;
258
258
  componentData.previews[previewKey].url = `${componentId}-${previewKey}.html`;
259
259
  }
260
- // Emit standalone client module for Playground live rendering
261
- console.log('Emmiting Module componentPath', componentId, componentPath);
262
- try {
263
- const clientModuleSource = (0, build_1.generateClientComponentSource)(componentPath);
264
- const clientModuleJs = yield (0, build_1.buildClientModule)(clientModuleSource, handoff);
265
- this.emitFile({
266
- type: 'asset',
267
- fileName: `${componentId}.module.js`,
268
- source: clientModuleJs,
269
- });
270
- }
271
- catch (error) {
272
- logger_1.Logger.warn(`Failed to build client module for ${componentId}: ${error}`);
273
- }
274
260
  // Format final HTML and update component data
275
261
  finalHtml = yield (0, html_1.formatHtml)(finalHtml);
276
262
  componentData.format = 'react';
@@ -66,6 +66,19 @@ var ComponentSegment;
66
66
  ComponentSegment["Previews"] = "previews";
67
67
  ComponentSegment["Validation"] = "validation";
68
68
  })(ComponentSegment || (exports.ComponentSegment = ComponentSegment = {}));
69
+ const ensureDefaultPreview = (data) => {
70
+ if (!data)
71
+ return;
72
+ if (!data.previews || Object.keys(data.previews).length === 0) {
73
+ data.previews = {
74
+ default: {
75
+ title: 'Default',
76
+ values: {},
77
+ url: '',
78
+ },
79
+ };
80
+ }
81
+ };
69
82
  /**
70
83
  * Returns a normalized build plan describing which component segments need rebuilding.
71
84
  *
@@ -200,7 +213,7 @@ function processComponents(handoff, id, segmentToProcess, options) {
200
213
  // those will override this default (standard "config overrides defaults" behavior).
201
214
  if (!restMetadata.figmaComponentId) {
202
215
  componentDefaults.previews = {
203
- generic: {
216
+ default: {
204
217
  title: 'Default',
205
218
  values: {},
206
219
  url: '',
@@ -246,6 +259,8 @@ function processComponents(handoff, id, segmentToProcess, options) {
246
259
  data.properties = existingData.properties;
247
260
  }
248
261
  }
262
+ // Components should always have at least one preview variation.
263
+ ensureDefaultPreview(data);
249
264
  // Build JS if needed (new build, validation missing, or explicit segment request).
250
265
  if (buildPlan.js) {
251
266
  data = yield (0, javascript_1.default)(data, handoff);
@@ -1,5 +1,4 @@
1
1
  import esbuild from 'esbuild';
2
- import Handoff from '../../index';
3
2
  /**
4
3
  * Default esbuild configuration for SSR builds
5
4
  */
@@ -26,20 +25,3 @@ export declare function createReactResolvePlugin(workingPath: string, handoffMod
26
25
  name: string;
27
26
  setup(build: any): void;
28
27
  };
29
- /**
30
- * Generates the entry source for a standalone client-side React component module.
31
- * The resulting module exports `render(container, props)` and `update(props)`
32
- * so the Playground can dynamically load and re-render it in an iframe.
33
- */
34
- export declare function generateClientComponentSource(componentPath: string): string;
35
- /**
36
- * Generates the entry source for a standalone CSF component module.
37
- * Replicates the story rendering logic (meta.render / story.render / meta.component)
38
- * so the Playground can render CSF components client-side.
39
- */
40
- export declare function generateCsfClientComponentSource(componentPath: string, storyKey: string): string;
41
- /**
42
- * Builds a self-contained browser ESM module from the given source code.
43
- * React and ReactDOM are bundled into the output so the module works standalone.
44
- */
45
- export declare function buildClientModule(sourceCode: string, handoff: Handoff): Promise<string>;
@@ -1,13 +1,4 @@
1
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
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
4
  };
@@ -15,12 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
6
  exports.DEFAULT_CLIENT_BUILD_CONFIG = exports.DEFAULT_SSR_BUILD_CONFIG = void 0;
16
7
  exports.resolveModule = resolveModule;
17
8
  exports.createReactResolvePlugin = createReactResolvePlugin;
18
- exports.generateClientComponentSource = generateClientComponentSource;
19
- exports.generateCsfClientComponentSource = generateCsfClientComponentSource;
20
- exports.buildClientModule = buildClientModule;
21
- const esbuild_1 = __importDefault(require("esbuild"));
22
9
  const path_1 = __importDefault(require("path"));
23
- const logger_1 = require("../../utils/logger");
24
10
  /**
25
11
  * Default esbuild configuration for SSR builds
26
12
  */
@@ -88,97 +74,3 @@ function createReactResolvePlugin(workingPath, handoffModulePath) {
88
74
  },
89
75
  };
90
76
  }
91
- function toForwardSlash(p) {
92
- return p.replace(/\\/g, '/');
93
- }
94
- /**
95
- * Generates the entry source for a standalone client-side React component module.
96
- * The resulting module exports `render(container, props)` and `update(props)`
97
- * so the Playground can dynamically load and re-render it in an iframe.
98
- */
99
- function generateClientComponentSource(componentPath) {
100
- return `
101
- import React from 'react';
102
- import { createRoot } from 'react-dom/client';
103
- import Component from '${toForwardSlash(componentPath)}';
104
-
105
- let root = null;
106
-
107
- export function render(container, props) {
108
- root = createRoot(container);
109
- root.render(React.createElement(Component, props));
110
- return root;
111
- }
112
-
113
- export function update(props) {
114
- if (root) {
115
- root.render(React.createElement(Component, props));
116
- }
117
- }
118
- `;
119
- }
120
- /**
121
- * Generates the entry source for a standalone CSF component module.
122
- * Replicates the story rendering logic (meta.render / story.render / meta.component)
123
- * so the Playground can render CSF components client-side.
124
- */
125
- function generateCsfClientComponentSource(componentPath, storyKey) {
126
- return `
127
- import React from 'react';
128
- import { createRoot } from 'react-dom/client';
129
- import * as stories from '${toForwardSlash(componentPath)}';
130
-
131
- const meta = stories.default || {};
132
- const story = stories['${storyKey}'];
133
-
134
- let root = null;
135
-
136
- function renderElement(props) {
137
- const storyRender = typeof story === 'function' ? story : (story && story.render);
138
- const renderFn = storyRender || meta.render;
139
- if (renderFn) {
140
- const result = renderFn(props);
141
- return React.isValidElement(result) ? result : React.createElement(meta.component, props);
142
- }
143
- if (meta.component) {
144
- return React.createElement(meta.component, props);
145
- }
146
- return React.createElement('pre', null, JSON.stringify(props, null, 2));
147
- }
148
-
149
- export function render(container, props) {
150
- root = createRoot(container);
151
- root.render(renderElement(props));
152
- return root;
153
- }
154
-
155
- export function update(props) {
156
- if (root) {
157
- root.render(renderElement(props));
158
- }
159
- }
160
- `;
161
- }
162
- /**
163
- * Builds a self-contained browser ESM module from the given source code.
164
- * React and ReactDOM are bundled into the output so the module works standalone.
165
- */
166
- function buildClientModule(sourceCode, handoff) {
167
- return __awaiter(this, void 0, void 0, function* () {
168
- var _a, _b;
169
- const config = Object.assign(Object.assign({}, exports.DEFAULT_CLIENT_BUILD_CONFIG), { logLevel: 'silent', stdin: {
170
- contents: sourceCode,
171
- resolveDir: process.cwd(),
172
- loader: 'tsx',
173
- }, plugins: [createReactResolvePlugin(handoff.workingPath, handoff.modulePath)] });
174
- const finalConfig = ((_b = (_a = handoff.config) === null || _a === void 0 ? void 0 : _a.hooks) === null || _b === void 0 ? void 0 : _b.clientBuildConfig)
175
- ? handoff.config.hooks.clientBuildConfig(config)
176
- : config;
177
- const result = yield esbuild_1.default.build(finalConfig);
178
- if (result.warnings.length > 0) {
179
- const messages = yield esbuild_1.default.formatMessages(result.warnings, { kind: 'warning', color: true });
180
- messages.forEach((msg) => logger_1.Logger.warn(msg));
181
- }
182
- return result.outputFiles[0].text;
183
- });
184
- }
@@ -21,9 +21,21 @@ const prettier_1 = __importDefault(require("prettier"));
21
21
  * @returns Trimmed HTML content
22
22
  */
23
23
  const trimPreview = (preview) => {
24
- const bodyEl = (0, node_html_parser_1.parse)(preview).querySelector('body');
25
- const code = bodyEl ? bodyEl.innerHTML.trim() : preview;
26
- return code;
24
+ if (typeof preview !== 'string') {
25
+ return '';
26
+ }
27
+ const normalizedPreview = preview.trim();
28
+ if (!normalizedPreview) {
29
+ return '';
30
+ }
31
+ try {
32
+ const bodyEl = (0, node_html_parser_1.parse)(normalizedPreview).querySelector('body');
33
+ return bodyEl ? bodyEl.innerHTML.trim() : normalizedPreview;
34
+ }
35
+ catch (_a) {
36
+ // Keep build resilient if parser fails on malformed HTML.
37
+ return normalizedPreview;
38
+ }
27
39
  };
28
40
  exports.trimPreview = trimPreview;
29
41
  /**
@@ -3,7 +3,6 @@ import { Types as HandoffTypes } from 'handoff-core';
3
3
  import { InlineConfig } from 'vite';
4
4
  import { SlotMetadata } from '../transformers/preview/component';
5
5
  import { ComponentListObject, TransformComponentTokensResult } from '../transformers/preview/types';
6
- import { PageListObject } from '../transformers/preview/page/types';
7
6
  import { ValidationResult } from './preview';
8
7
  export interface ImageStyle {
9
8
  name: string;
@@ -114,11 +113,6 @@ export interface Config {
114
113
  * @example ["components/button", "components/input"]
115
114
  */
116
115
  components?: string[];
117
- /**
118
- * Array of page paths to be included in the build
119
- * @example ["pages/homepage", "pages/about"]
120
- */
121
- pages?: string[];
122
116
  };
123
117
  /**
124
118
  * Configuration for asset zip file download links
@@ -136,16 +130,6 @@ export interface Config {
136
130
  */
137
131
  logos?: string;
138
132
  };
139
- /**
140
- * Platform project ID (UUID). Set by `platform:init` to link this
141
- * local project with a remote handoff-service project.
142
- */
143
- platformProjectId?: string;
144
- /**
145
- * Platform base URL (e.g. "http://localhost:3000" or "https://handoff.example.com").
146
- * Set by `platform:init` so subsequent sync commands know where to connect.
147
- */
148
- platformUrl?: string;
149
133
  /**
150
134
  * Configuration hooks for extending functionality
151
135
  */
@@ -277,9 +261,6 @@ export interface RuntimeConfig {
277
261
  components: {
278
262
  [id: string]: ComponentListObject;
279
263
  };
280
- pages: {
281
- [id: string]: PageListObject;
282
- };
283
264
  };
284
265
  options: {
285
266
  [key: string]: RuntimeConfigComponentOptions;
@@ -4,3 +4,18 @@
4
4
  * @returns A filesystem-safe string suitable for use as a directory name
5
5
  */
6
6
  export declare function generateFilesystemSafeId(workingPath: string): string;
7
+ /**
8
+ * Normalize a filesystem path for cross-platform string comparison.
9
+ * - Resolves to absolute path
10
+ * - Normalizes separators to forward slashes
11
+ * - Lowercases on Windows where paths are case-insensitive
12
+ */
13
+ export declare function normalizePathForCompare(inputPath: string): string;
14
+ /**
15
+ * True when both paths resolve to the same normalized filesystem path.
16
+ */
17
+ export declare function arePathsEqual(pathA: string, pathB: string): boolean;
18
+ /**
19
+ * True when childPath is equal to or within parentPath.
20
+ */
21
+ export declare function isPathInside(childPath: string, parentPath: string): boolean;
@@ -1,6 +1,13 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.generateFilesystemSafeId = generateFilesystemSafeId;
7
+ exports.normalizePathForCompare = normalizePathForCompare;
8
+ exports.arePathsEqual = arePathsEqual;
9
+ exports.isPathInside = isPathInside;
10
+ const path_1 = __importDefault(require("path"));
4
11
  /**
5
12
  * Generates a filesystem-safe directory name from an absolute path
6
13
  * @param workingPath - The absolute working path to transform
@@ -26,3 +33,27 @@ function generateFilesystemSafeId(workingPath) {
26
33
  }
27
34
  return safeId;
28
35
  }
36
+ /**
37
+ * Normalize a filesystem path for cross-platform string comparison.
38
+ * - Resolves to absolute path
39
+ * - Normalizes separators to forward slashes
40
+ * - Lowercases on Windows where paths are case-insensitive
41
+ */
42
+ function normalizePathForCompare(inputPath) {
43
+ const normalized = path_1.default.resolve(inputPath).replace(/\\/g, '/');
44
+ return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
45
+ }
46
+ /**
47
+ * True when both paths resolve to the same normalized filesystem path.
48
+ */
49
+ function arePathsEqual(pathA, pathB) {
50
+ return normalizePathForCompare(pathA) === normalizePathForCompare(pathB);
51
+ }
52
+ /**
53
+ * True when childPath is equal to or within parentPath.
54
+ */
55
+ function isPathInside(childPath, parentPath) {
56
+ const normalizedChild = normalizePathForCompare(childPath);
57
+ const normalizedParent = normalizePathForCompare(parentPath);
58
+ return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}/`);
59
+ }