handoff-app 1.1.0-experimental.2 → 1.1.0-experimental.3

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 (70) hide show
  1. package/dist/app-builder/build.d.ts +2 -0
  2. package/dist/app-builder/build.js +12 -7
  3. package/dist/app-builder/isr-paths.d.ts +9 -0
  4. package/dist/app-builder/isr-paths.js +29 -0
  5. package/dist/app-builder/watchers.d.ts +2 -2
  6. package/dist/app-builder/watchers.js +24 -24
  7. package/dist/commands/index.js +2 -0
  8. package/dist/commands/isr/pattern.d.ts +10 -0
  9. package/dist/commands/isr/pattern.js +63 -0
  10. package/dist/config/runtime.d.ts +3 -3
  11. package/dist/config/runtime.js +28 -28
  12. package/dist/index.d.ts +1 -0
  13. package/dist/pipeline/index.d.ts +1 -1
  14. package/dist/pipeline/index.js +3 -3
  15. package/dist/pipeline/patterns.d.ts +6 -0
  16. package/dist/pipeline/{pages.js → patterns.js} +6 -6
  17. package/dist/transformers/plugins/csf-render.js +16 -9
  18. package/dist/transformers/plugins/handlebars-previews.js +2 -0
  19. package/dist/transformers/preview/component/css.d.ts +1 -0
  20. package/dist/transformers/preview/component.d.ts +4 -0
  21. package/dist/transformers/preview/pattern/api.d.ts +8 -0
  22. package/dist/transformers/preview/{page → pattern}/api.js +25 -25
  23. package/dist/transformers/preview/pattern/builder.d.ts +7 -0
  24. package/dist/transformers/preview/{page → pattern}/builder.js +53 -51
  25. package/dist/transformers/preview/pattern/types.d.ts +47 -0
  26. package/dist/transformers/preview/types.d.ts +4 -0
  27. package/dist/transformers/utils/handlebars.d.ts +1 -0
  28. package/dist/transformers/utils/handlebars.js +5 -1
  29. package/dist/types/config.d.ts +6 -6
  30. package/docs/pages.md +22 -22
  31. package/handoff.config.js +1 -1
  32. package/package.json +1 -1
  33. package/src/app/components/{Page/PageLists.tsx → Pattern/PatternLists.tsx} +35 -35
  34. package/src/app/components/{Page → Pattern}/types.ts +1 -1
  35. package/src/app/components/Playground/PlaygroundBuilder.tsx +51 -5
  36. package/src/app/components/Playground/Preview.tsx +15 -3
  37. package/src/app/components/Playground/fields/Field.tsx +16 -1
  38. package/src/app/components/Playground/fields/SelectField.tsx +45 -0
  39. package/src/app/components/Playground/types.ts +16 -0
  40. package/src/app/components/util/index.ts +22 -22
  41. package/src/app/pages/system/{page/[page] → pattern/[pattern]}/index.tsx +48 -48
  42. package/src/app/pages/system/{page → pattern}/index.tsx +21 -21
  43. package/src/app-builder/build.ts +13 -9
  44. package/src/app-builder/isr-paths.ts +29 -0
  45. package/src/app-builder/watchers.ts +22 -22
  46. package/src/commands/index.ts +2 -0
  47. package/src/commands/isr/pattern.ts +68 -0
  48. package/src/config/runtime.ts +27 -27
  49. package/src/index.ts +1 -1
  50. package/src/pipeline/index.ts +1 -1
  51. package/src/pipeline/patterns.ts +10 -0
  52. package/src/transformers/plugins/csf-render.ts +13 -6
  53. package/src/transformers/plugins/handlebars-previews.ts +1 -0
  54. package/src/transformers/preview/component.ts +2 -0
  55. package/src/transformers/preview/pattern/api.ts +65 -0
  56. package/src/transformers/preview/{page → pattern}/builder.ts +57 -55
  57. package/src/transformers/preview/pattern/types.ts +46 -0
  58. package/src/transformers/preview/types.ts +4 -0
  59. package/src/transformers/utils/handlebars.ts +6 -2
  60. package/src/types/config.ts +6 -6
  61. package/test_components/example-react/template.tsx +1 -4
  62. package/{test_pages → test_patterns}/boilerplate/boilerplate.json +3 -0
  63. package/dist/pipeline/pages.d.ts +0 -6
  64. package/dist/transformers/preview/page/api.d.ts +0 -8
  65. package/dist/transformers/preview/page/builder.d.ts +0 -7
  66. package/dist/transformers/preview/page/types.d.ts +0 -38
  67. package/src/pipeline/pages.ts +0 -10
  68. package/src/transformers/preview/page/api.ts +0 -65
  69. package/src/transformers/preview/page/types.ts +0 -37
  70. /package/dist/transformers/preview/{page → pattern}/types.js +0 -0
@@ -3,10 +3,12 @@ import Handoff from '..';
3
3
  * Build the Next.js documentation application.
4
4
  * @param copyOnlyPaths - If set, do not remove output dir; copy only these paths (relative to out/) into existing output (merge).
5
5
  * @param deployDir - If set, copy output to this directory instead of default (workingPath/sitesDirectory/projectId).
6
+ * @param skipPatterns - If true, skip the pattern build step (useful when patterns were pre-processed for ISR).
6
7
  */
7
8
  declare const buildApp: (handoff: Handoff, skipComponents?: boolean, options?: {
8
9
  copyOnlyPaths?: string[];
9
10
  deployDir?: string;
11
+ skipPatterns?: boolean;
10
12
  }) => Promise<void>;
11
13
  /**
12
14
  * Watch the Next.js application.
@@ -17,14 +17,14 @@ const cross_spawn_1 = __importDefault(require("cross-spawn"));
17
17
  const fs_extra_1 = __importDefault(require("fs-extra"));
18
18
  const path_1 = __importDefault(require("path"));
19
19
  const components_1 = require("../pipeline/components");
20
- const pages_1 = require("../pipeline/pages");
20
+ const patterns_1 = require("../pipeline/patterns");
21
21
  const builder_1 = __importDefault(require("../transformers/preview/component/builder"));
22
22
  const css_1 = require("../transformers/preview/component/css");
23
23
  const javascript_1 = require("../transformers/preview/component/javascript");
24
24
  const logger_1 = require("../utils/logger");
25
25
  const client_config_1 = require("./client-config");
26
26
  const paths_1 = require("./paths");
27
- const builder_2 = require("../transformers/preview/page/builder");
27
+ const builder_2 = require("../transformers/preview/pattern/builder");
28
28
  const watchers_1 = require("./watchers");
29
29
  const websocket_1 = require("./websocket");
30
30
  /**
@@ -85,9 +85,12 @@ const initializeProjectApp = (handoff) => __awaiter(void 0, void 0, void 0, func
85
85
  * Build the Next.js documentation application.
86
86
  * @param copyOnlyPaths - If set, do not remove output dir; copy only these paths (relative to out/) into existing output (merge).
87
87
  * @param deployDir - If set, copy output to this directory instead of default (workingPath/sitesDirectory/projectId).
88
+ * @param skipPatterns - If true, skip the pattern build step (useful when patterns were pre-processed for ISR).
88
89
  */
89
90
  const buildApp = (handoff, skipComponents, options) => __awaiter(void 0, void 0, void 0, function* () {
91
+ var _a;
90
92
  skipComponents = skipComponents !== null && skipComponents !== void 0 ? skipComponents : false;
93
+ const skipPatterns = (_a = options === null || options === void 0 ? void 0 : options.skipPatterns) !== null && _a !== void 0 ? _a : false;
91
94
  const copyOnlyPaths = options === null || options === void 0 ? void 0 : options.copyOnlyPaths;
92
95
  const deployDir = options === null || options === void 0 ? void 0 : options.deployDir;
93
96
  // Perform cleanup
@@ -96,8 +99,10 @@ const buildApp = (handoff, skipComponents, options) => __awaiter(void 0, void 0,
96
99
  if (!skipComponents) {
97
100
  yield (0, components_1.buildComponents)(handoff);
98
101
  }
99
- // Build pages (must run after components so rendered outputs exist)
100
- yield (0, pages_1.buildPages)(handoff);
102
+ // Build patterns (must run after components so rendered outputs exist)
103
+ if (!skipPatterns) {
104
+ yield (0, patterns_1.buildPatterns)(handoff);
105
+ }
101
106
  // Prepare app
102
107
  const appPath = yield initializeProjectApp(handoff);
103
108
  yield (0, client_config_1.persistClientConfig)(handoff);
@@ -154,8 +159,8 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
154
159
  yield (0, builder_1.default)(handoff, undefined, undefined, { useCache: true });
155
160
  yield (0, javascript_1.buildMainJS)(handoff);
156
161
  yield (0, css_1.buildMainCss)(handoff);
157
- // Build pages after components
158
- yield (0, builder_2.processPages)(handoff);
162
+ // Build patterns after components
163
+ yield (0, builder_2.processPatterns)(handoff);
159
164
  const appPath = yield initializeProjectApp(handoff);
160
165
  // Persist client configuration
161
166
  yield (0, client_config_1.persistClientConfig)(handoff);
@@ -200,7 +205,7 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
200
205
  (0, watchers_1.watchRuntimeConfiguration)(handoff, state);
201
206
  (0, watchers_1.watchGlobalEntries)(handoff, state, chokidarConfig);
202
207
  (0, watchers_1.watchPages)(handoff, chokidarConfig);
203
- (0, watchers_1.watchRuntimePages)(handoff, state);
208
+ (0, watchers_1.watchRuntimePatterns)(handoff, state);
204
209
  });
205
210
  exports.watchApp = watchApp;
206
211
  /**
@@ -42,3 +42,12 @@ export declare function resolveComponentAffectedOutputPaths(componentId: string)
42
42
  * Resolve affected output paths for multiple components (union).
43
43
  */
44
44
  export declare function resolveComponentAffectedOutputPathsBatch(componentIds: string[]): string[];
45
+ /**
46
+ * Resolve affected static output file paths for a pattern ISR update.
47
+ * Returns paths under out/ for the pattern API JSON and summary.
48
+ */
49
+ export declare function resolvePatternAffectedOutputPaths(patternId: string): string[];
50
+ /**
51
+ * Resolve affected output paths for multiple patterns (union).
52
+ */
53
+ export declare function resolvePatternAffectedOutputPathsBatch(patternIds: string[]): string[];
@@ -10,6 +10,8 @@ exports.routePathToOutputPaths = routePathToOutputPaths;
10
10
  exports.resolveAffectedOutputPaths = resolveAffectedOutputPaths;
11
11
  exports.resolveComponentAffectedOutputPaths = resolveComponentAffectedOutputPaths;
12
12
  exports.resolveComponentAffectedOutputPathsBatch = resolveComponentAffectedOutputPathsBatch;
13
+ exports.resolvePatternAffectedOutputPaths = resolvePatternAffectedOutputPaths;
14
+ exports.resolvePatternAffectedOutputPathsBatch = resolvePatternAffectedOutputPathsBatch;
13
15
  /**
14
16
  * ISR path resolver: computes which static output files (under out/) are affected
15
17
  * for a given page path or "menu changed", for use with partial deploy.
@@ -211,3 +213,30 @@ function resolveComponentAffectedOutputPathsBatch(componentIds) {
211
213
  }
212
214
  return unique;
213
215
  }
216
+ /**
217
+ * Resolve affected static output file paths for a pattern ISR update.
218
+ * Returns paths under out/ for the pattern API JSON and summary.
219
+ */
220
+ function resolvePatternAffectedOutputPaths(patternId) {
221
+ return [
222
+ `api/pattern/${patternId}.json`,
223
+ 'api/patterns.json',
224
+ ];
225
+ }
226
+ /**
227
+ * Resolve affected output paths for multiple patterns (union).
228
+ */
229
+ function resolvePatternAffectedOutputPathsBatch(patternIds) {
230
+ const out = ['api/patterns.json'];
231
+ for (const id of patternIds) {
232
+ out.push(`api/pattern/${id}.json`);
233
+ }
234
+ // ES5-compatible alternative to [...new Set(out)]
235
+ var unique = [];
236
+ for (var i = 0; i < out.length; i++) {
237
+ if (unique.indexOf(out[i]) === -1) {
238
+ unique.push(out[i]);
239
+ }
240
+ }
241
+ return unique;
242
+ }
@@ -40,9 +40,9 @@ export declare const getRuntimeComponentsPathsToWatch: (handoff: Handoff) => Map
40
40
  */
41
41
  export declare const watchRuntimeComponents: (handoff: Handoff, state: WatcherState, runtimeComponentPathsToWatch: Map<string, keyof ComponentListObject["entries"]>) => void;
42
42
  /**
43
- * Watches runtime page config files for changes and triggers page rebuilds.
43
+ * Watches runtime pattern config files for changes and triggers pattern rebuilds.
44
44
  */
45
- export declare const watchRuntimePages: (handoff: Handoff, state: WatcherState) => void;
45
+ export declare const watchRuntimePatterns: (handoff: Handoff, state: WatcherState) => void;
46
46
  /**
47
47
  * Watches the runtime configuration for changes.
48
48
  */
@@ -35,14 +35,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.watchRuntimeConfiguration = exports.watchRuntimePages = exports.watchRuntimeComponents = exports.getRuntimeComponentsPathsToWatch = exports.mapEntryTypeToSegment = exports.watchGlobalEntries = exports.watchPages = exports.watchAppSource = exports.watchPublicDirectory = void 0;
38
+ exports.watchRuntimeConfiguration = exports.watchRuntimePatterns = exports.watchRuntimeComponents = exports.getRuntimeComponentsPathsToWatch = exports.mapEntryTypeToSegment = exports.watchGlobalEntries = exports.watchPages = exports.watchAppSource = exports.watchPublicDirectory = void 0;
39
39
  const chokidar_1 = __importDefault(require("chokidar"));
40
40
  const fs_extra_1 = __importDefault(require("fs-extra"));
41
41
  const path_1 = __importDefault(require("path"));
42
42
  const css_1 = require("../transformers/preview/component/css");
43
43
  const javascript_1 = require("../transformers/preview/component/javascript");
44
44
  const builder_1 = __importStar(require("../transformers/preview/component/builder"));
45
- const builder_2 = require("../transformers/preview/page/builder");
45
+ const builder_2 = require("../transformers/preview/pattern/builder");
46
46
  const logger_1 = require("../utils/logger");
47
47
  const client_config_1 = require("./client-config");
48
48
  const paths_1 = require("./paths");
@@ -262,31 +262,31 @@ const watchRuntimeComponents = (handoff, state, runtimeComponentPathsToWatch) =>
262
262
  };
263
263
  exports.watchRuntimeComponents = watchRuntimeComponents;
264
264
  /**
265
- * Watches runtime page config files for changes and triggers page rebuilds.
265
+ * Watches runtime pattern config files for changes and triggers pattern rebuilds.
266
266
  */
267
- const watchRuntimePages = (handoff, state) => {
267
+ const watchRuntimePatterns = (handoff, state) => {
268
268
  var _a, _b, _c, _d, _e;
269
- const runtimePages = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.pages) !== null && _c !== void 0 ? _c : {};
270
- const pageConfigPaths = handoff.getConfigFilePaths().filter((configPath) => {
269
+ 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 : {};
270
+ const patternConfigPaths = handoff.getConfigFilePaths().filter((configPath) => {
271
271
  const dirName = path_1.default.basename(path_1.default.dirname(configPath));
272
- return dirName in runtimePages;
272
+ return dirName in runtimePatterns;
273
273
  });
274
- // Also watch page directories directly
275
- const pageIds = Object.keys(runtimePages);
276
- if (pageIds.length === 0 && pageConfigPaths.length === 0)
274
+ // Also watch pattern directories directly
275
+ const patternIds = Object.keys(runtimePatterns);
276
+ if (patternIds.length === 0 && patternConfigPaths.length === 0)
277
277
  return;
278
- const pathsToWatch = pageConfigPaths.length > 0 ? pageConfigPaths : [];
279
- // Add page config entry paths from the config
280
- for (const pageId of pageIds) {
281
- const page = runtimePages[pageId];
282
- if (page.path && fs_extra_1.default.existsSync(page.path)) {
283
- pathsToWatch.push(page.path);
278
+ const pathsToWatch = patternConfigPaths.length > 0 ? patternConfigPaths : [];
279
+ // Add pattern config entry paths from the config
280
+ for (const patternId of patternIds) {
281
+ const pattern = runtimePatterns[patternId];
282
+ if (pattern.path && fs_extra_1.default.existsSync(pattern.path)) {
283
+ pathsToWatch.push(pattern.path);
284
284
  }
285
285
  }
286
- // Watch the page entry directories from config
287
- if ((_e = (_d = handoff.config.entries) === null || _d === void 0 ? void 0 : _d.pages) === null || _e === void 0 ? void 0 : _e.length) {
288
- for (const pagePath of handoff.config.entries.pages) {
289
- const resolvedPath = path_1.default.resolve(handoff.workingPath, pagePath);
286
+ // Watch the pattern entry directories from config
287
+ if ((_e = (_d = handoff.config.entries) === null || _d === void 0 ? void 0 : _d.patterns) === null || _e === void 0 ? void 0 : _e.length) {
288
+ for (const patternPath of handoff.config.entries.patterns) {
289
+ const resolvedPath = path_1.default.resolve(handoff.workingPath, patternPath);
290
290
  if (fs_extra_1.default.existsSync(resolvedPath)) {
291
291
  pathsToWatch.push(resolvedPath);
292
292
  }
@@ -302,12 +302,12 @@ const watchRuntimePages = (handoff, state) => {
302
302
  if (!state.debounce) {
303
303
  state.debounce = true;
304
304
  try {
305
- logger_1.Logger.warn('Page config changed. Rebuilding pages...');
305
+ logger_1.Logger.warn('Pattern config changed. Rebuilding patterns...');
306
306
  handoff.reload();
307
- yield (0, builder_2.processPages)(handoff);
307
+ yield (0, builder_2.processPatterns)(handoff);
308
308
  }
309
309
  catch (e) {
310
- logger_1.Logger.error('Error processing pages:', e);
310
+ logger_1.Logger.error('Error processing patterns:', e);
311
311
  }
312
312
  finally {
313
313
  state.debounce = false;
@@ -317,7 +317,7 @@ const watchRuntimePages = (handoff, state) => {
317
317
  }
318
318
  }));
319
319
  };
320
- exports.watchRuntimePages = watchRuntimePages;
320
+ exports.watchRuntimePatterns = watchRuntimePatterns;
321
321
  /**
322
322
  * Watches the runtime configuration for changes.
323
323
  */
@@ -14,6 +14,7 @@ const fetch_1 = __importDefault(require("./fetch"));
14
14
  const init_1 = __importDefault(require("./init"));
15
15
  const component_1 = __importDefault(require("./isr/component"));
16
16
  const pages_2 = __importDefault(require("./isr/pages"));
17
+ const pattern_1 = __importDefault(require("./isr/pattern"));
17
18
  const component_2 = __importDefault(require("./make/component"));
18
19
  const page_1 = __importDefault(require("./make/page"));
19
20
  const template_1 = __importDefault(require("./make/template"));
@@ -36,6 +37,7 @@ exports.commands = [
36
37
  init_1.default,
37
38
  pages_2.default,
38
39
  component_1.default,
40
+ pattern_1.default,
39
41
  page_1.default,
40
42
  component_2.default,
41
43
  template_1.default,
@@ -0,0 +1,10 @@
1
+ import { CommandModule } from 'yargs';
2
+ import { SharedArgs } from '../types';
3
+ export interface IsrPatternArgs extends SharedArgs {
4
+ id?: string;
5
+ ids?: string[];
6
+ partial?: boolean;
7
+ deployDir?: string;
8
+ }
9
+ declare const command: CommandModule<{}, IsrPatternArgs>;
10
+ export default command;
@@ -0,0 +1,63 @@
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
+ const __1 = __importDefault(require("../../"));
16
+ const isr_paths_1 = require("../../app-builder/isr-paths");
17
+ const builder_1 = require("../../transformers/preview/pattern/builder");
18
+ const utils_1 = require("../utils");
19
+ const command = {
20
+ command: 'isr:pattern',
21
+ describe: 'ISR for pattern changes: rebuild one or more patterns, then rebuild app and optionally deploy only affected routes',
22
+ builder: (yargs) => {
23
+ return (0, utils_1.getSharedOptions)(yargs)
24
+ .option('id', {
25
+ describe: 'Pattern ID to rebuild (e.g. boilerplate)',
26
+ type: 'string',
27
+ })
28
+ .option('ids', {
29
+ describe: 'Comma-separated pattern IDs to rebuild',
30
+ type: 'string',
31
+ coerce: (arg) => (arg ? arg.split(',').map((p) => p.trim()) : []),
32
+ })
33
+ .option('partial', {
34
+ describe: 'Partial deploy: copy only affected pattern output files into existing site output',
35
+ type: 'boolean',
36
+ default: false,
37
+ })
38
+ .option('deploy-dir', {
39
+ describe: 'Copy output to this directory instead of default site output',
40
+ type: 'string',
41
+ });
42
+ },
43
+ handler: (args) => __awaiter(void 0, void 0, void 0, function* () {
44
+ var _a;
45
+ const handoff = new __1.default(args.debug, args.force);
46
+ handoff.init();
47
+ const ids = args.id ? [args.id] : (_a = args.ids) !== null && _a !== void 0 ? _a : [];
48
+ if (ids.length === 0) {
49
+ throw new Error('Either --id or --ids is required');
50
+ }
51
+ for (const id of ids) {
52
+ yield (0, builder_1.processPatterns)(handoff, id);
53
+ }
54
+ const copyOnlyPaths = args.partial
55
+ ? (0, isr_paths_1.resolvePatternAffectedOutputPathsBatch)(ids)
56
+ : undefined;
57
+ const buildOptions = (copyOnlyPaths === null || copyOnlyPaths === void 0 ? void 0 : copyOnlyPaths.length) || args.deployDir
58
+ ? { copyOnlyPaths: (copyOnlyPaths === null || copyOnlyPaths === void 0 ? void 0 : copyOnlyPaths.length) ? copyOnlyPaths : undefined, deployDir: args.deployDir }
59
+ : undefined;
60
+ yield handoff.build(true, buildOptions);
61
+ }),
62
+ };
63
+ exports.default = command;
@@ -30,10 +30,10 @@ export declare const initRuntimeConfig: (handoff: HandoffContext) => [runtimeCon
30
30
  */
31
31
  export declare const getComponentsForPath: (searchPath: string) => string[];
32
32
  /**
33
- * Returns a list of page directories for a given path.
34
- * Mirrors getComponentsForPath but for page definitions.
33
+ * Returns a list of pattern directories for a given path.
34
+ * Mirrors getComponentsForPath but for pattern definitions.
35
35
  */
36
- export declare const getPagesForPath: (searchPath: string) => string[];
36
+ export declare const getPatternsForPath: (searchPath: string) => string[];
37
37
  /**
38
38
  * Recursively converts all keys and string values in an object to lowercase.
39
39
  */
@@ -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.getPatternsForPath = 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");
@@ -24,7 +24,7 @@ const initRuntimeConfig = (handoff) => {
24
24
  scss: undefined,
25
25
  js: undefined,
26
26
  components: {},
27
- pages: {},
27
+ patterns: {},
28
28
  },
29
29
  };
30
30
  if (!!((_a = handoff.config.entries) === null || _a === void 0 ? void 0 : _a.scss)) {
@@ -90,45 +90,45 @@ const initRuntimeConfig = (handoff) => {
90
90
  result.entries.components[component.id] = component;
91
91
  }
92
92
  }
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)));
93
+ if ((_k = (_j = handoff.config.entries) === null || _j === void 0 ? void 0 : _j.patterns) === null || _k === void 0 ? void 0 : _k.length) {
94
+ const patternPaths = handoff.config.entries.patterns.flatMap(exports.getPatternsForPath);
95
+ for (const patternPath of patternPaths) {
96
+ const resolvedPatternPath = path_1.default.resolve(handoff.workingPath, patternPath);
97
+ const patternBaseName = path_1.default.basename(resolvedPatternPath);
98
+ const possibleConfigFiles = [`${patternBaseName}.json`, `${patternBaseName}.js`, `${patternBaseName}.cjs`];
99
+ const configFileName = possibleConfigFiles.find((file) => fs_extra_1.default.existsSync(path_1.default.resolve(resolvedPatternPath, file)));
100
100
  if (!configFileName) {
101
- logger_1.Logger.warn(`Missing page config: ${path_1.default.resolve(resolvedPagePath, possibleConfigFiles.join(' or '))}`);
101
+ logger_1.Logger.warn(`Missing pattern config: ${path_1.default.resolve(resolvedPatternPath, possibleConfigFiles.join(' or '))}`);
102
102
  continue;
103
103
  }
104
- const resolvedPageConfigPath = path_1.default.resolve(resolvedPagePath, configFileName);
105
- configFiles.push(resolvedPageConfigPath);
106
- let page;
104
+ const resolvedPatternConfigPath = path_1.default.resolve(resolvedPatternPath, configFileName);
105
+ configFiles.push(resolvedPatternConfigPath);
106
+ let pattern;
107
107
  try {
108
108
  if (configFileName.endsWith('.json')) {
109
- const pageJson = fs_extra_1.default.readFileSync(resolvedPageConfigPath, 'utf8');
110
- page = JSON.parse(pageJson);
109
+ const patternJson = fs_extra_1.default.readFileSync(resolvedPatternConfigPath, 'utf8');
110
+ pattern = JSON.parse(patternJson);
111
111
  }
112
112
  else {
113
- delete require.cache[require.resolve(resolvedPageConfigPath)];
114
- const importedPage = require(resolvedPageConfigPath);
115
- page = importedPage.default || importedPage;
113
+ delete require.cache[require.resolve(resolvedPatternConfigPath)];
114
+ const importedPattern = require(resolvedPatternConfigPath);
115
+ pattern = importedPattern.default || importedPattern;
116
116
  }
117
117
  }
118
118
  catch (err) {
119
- logger_1.Logger.error(`Failed to read or parse page config: ${resolvedPageConfigPath}`, err);
119
+ logger_1.Logger.error(`Failed to read or parse pattern config: ${resolvedPatternConfigPath}`, err);
120
120
  continue;
121
121
  }
122
- page.id = pageBaseName;
122
+ pattern.id = patternBaseName;
123
123
  // Validate that referenced components exist
124
- if (page.components) {
125
- for (const componentId of page.components) {
124
+ if (pattern.components) {
125
+ for (const componentId of pattern.components) {
126
126
  if (!result.entries.components[componentId]) {
127
- logger_1.Logger.warn(`Page '${page.id}' references unknown component '${componentId}'`);
127
+ logger_1.Logger.warn(`Pattern '${pattern.id}' references unknown component '${componentId}'`);
128
128
  }
129
129
  }
130
130
  }
131
- result.entries.pages[page.id] = page;
131
+ result.entries.patterns[pattern.id] = pattern;
132
132
  }
133
133
  }
134
134
  return [result, Array.from(configFiles)];
@@ -171,10 +171,10 @@ const getComponentsForPath = (searchPath) => {
171
171
  };
172
172
  exports.getComponentsForPath = getComponentsForPath;
173
173
  /**
174
- * Returns a list of page directories for a given path.
175
- * Mirrors getComponentsForPath but for page definitions.
174
+ * Returns a list of pattern directories for a given path.
175
+ * Mirrors getComponentsForPath but for pattern definitions.
176
176
  */
177
- const getPagesForPath = (searchPath) => {
177
+ const getPatternsForPath = (searchPath) => {
178
178
  const dirName = path_1.default.basename(searchPath);
179
179
  const possibleConfigFiles = [`${dirName}.json`, `${dirName}.js`, `${dirName}.cjs`];
180
180
  const hasOwnConfig = possibleConfigFiles.some((file) => fs_extra_1.default.existsSync(path_1.default.resolve(searchPath, file)));
@@ -190,7 +190,7 @@ const getPagesForPath = (searchPath) => {
190
190
  }
191
191
  return [searchPath];
192
192
  };
193
- exports.getPagesForPath = getPagesForPath;
193
+ exports.getPatternsForPath = getPatternsForPath;
194
194
  /**
195
195
  * Recursively converts all keys and string values in an object to lowercase.
196
196
  */
package/dist/index.d.ts CHANGED
@@ -29,6 +29,7 @@ declare class Handoff {
29
29
  build(skipComponents?: boolean, options?: {
30
30
  copyOnlyPaths?: string[];
31
31
  deployDir?: string;
32
+ skipPatterns?: boolean;
32
33
  }): Promise<Handoff>;
33
34
  ejectConfig(): Promise<Handoff>;
34
35
  ejectPages(): Promise<Handoff>;
@@ -2,7 +2,7 @@ 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';
5
+ export { buildPatterns } from './patterns';
6
6
  /**
7
7
  * Run the entire Figma data pipeline:
8
8
  * 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.buildPatterns = 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,8 @@ 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; } });
29
+ var patterns_1 = require("./patterns");
30
+ Object.defineProperty(exports, "buildPatterns", { enumerable: true, get: function () { return patterns_1.buildPatterns; } });
31
31
  /**
32
32
  * Run the entire Figma data pipeline:
33
33
  * 1. Validate runtime requirements
@@ -0,0 +1,6 @@
1
+ import Handoff from '..';
2
+ /**
3
+ * Builds pattern previews by running the pattern processor.
4
+ * Must be called after buildComponents so component outputs exist.
5
+ */
6
+ export declare const buildPatterns: (handoff: Handoff) => Promise<void>;
@@ -9,13 +9,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.buildPages = void 0;
13
- const builder_1 = require("../transformers/preview/page/builder");
12
+ exports.buildPatterns = void 0;
13
+ const builder_1 = require("../transformers/preview/pattern/builder");
14
14
  /**
15
- * Builds page previews by running the page processor.
15
+ * Builds pattern previews by running the pattern processor.
16
16
  * Must be called after buildComponents so component outputs exist.
17
17
  */
18
- const buildPages = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
19
- yield (0, builder_1.processPages)(handoff);
18
+ const buildPatterns = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
19
+ yield (0, builder_1.processPatterns)(handoff);
20
20
  });
21
- exports.buildPages = buildPages;
21
+ exports.buildPatterns = buildPatterns;
@@ -64,7 +64,7 @@ function createSlotMetadata(key, argType, value) {
64
64
  var _a, _b;
65
65
  const controlType = typeof (argType === null || argType === void 0 ? void 0 : argType.control) === 'string' ? argType.control : (_a = argType === null || argType === void 0 ? void 0 : argType.control) === null || _a === void 0 ? void 0 : _a.type;
66
66
  const type = inferSlotType(controlType, value);
67
- return {
67
+ const metadata = {
68
68
  id: key,
69
69
  name: (argType === null || argType === void 0 ? void 0 : argType.name) || (0, lodash_1.startCase)(key),
70
70
  description: (argType === null || argType === void 0 ? void 0 : argType.description) || '',
@@ -75,6 +75,11 @@ function createSlotMetadata(key, argType, value) {
75
75
  required: !!(argType === null || argType === void 0 ? void 0 : argType.required),
76
76
  },
77
77
  };
78
+ // Capture select/radio options so the Playground can render a proper dropdown
79
+ if (Array.isArray(argType === null || argType === void 0 ? void 0 : argType.options) && argType.options.length > 0) {
80
+ metadata.options = argType.options;
81
+ }
82
+ return metadata;
78
83
  }
79
84
  function getStoryEntries(moduleExports) {
80
85
  return Object.entries(moduleExports).filter(([key, value]) => {
@@ -119,15 +124,17 @@ function safeRenderToHtml(meta, story, args) {
119
124
  return `<div style="padding:1rem;color:#b91c1c;font-family:monospace;font-size:13px">Render error: ${String(error)}</div>`;
120
125
  }
121
126
  }
122
- function createHtmlDocument(componentId, previewTitle, renderedHtml) {
123
- var _a, _b, _c;
127
+ function createHtmlDocument(componentId, previewTitle, renderedHtml, previewCss) {
128
+ var _a;
129
+ const basePath = (_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : '';
130
+ const previewCssLink = previewCss ? `\n <link rel="stylesheet" href="${previewCss}" />` : '';
124
131
  return `<!DOCTYPE html>
125
132
  <html lang="en">
126
133
  <head>
127
134
  <meta charset="UTF-8" />
128
- <link rel="stylesheet" href="${(_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : ''}/api/component/main.css" />
129
- <link rel="stylesheet" href="${(_b = process.env.HANDOFF_APP_BASE_PATH) !== null && _b !== void 0 ? _b : ''}/api/component/${componentId}.css" />
130
- <link rel="stylesheet" href="${(_c = process.env.HANDOFF_APP_BASE_PATH) !== null && _c !== void 0 ? _c : ''}/assets/css/preview.css" />
135
+ <link rel="stylesheet" href="${basePath}/api/component/main.css" />
136
+ <link rel="stylesheet" href="${basePath}/api/component/${componentId}.css" />
137
+ <link rel="stylesheet" href="${basePath}/assets/css/preview.css" />${previewCssLink}
131
138
  <title>${previewTitle}</title>
132
139
  </head>
133
140
  <body>
@@ -154,7 +161,7 @@ function csfRenderPlugin(componentData, _documentationComponents, handoff) {
154
161
  },
155
162
  generateBundle(_, bundle) {
156
163
  return __awaiter(this, void 0, void 0, function* () {
157
- var _a, _b, _c, _d, _e, _f;
164
+ var _a, _b, _c, _d, _e, _f, _g, _h;
158
165
  for (const [fileName, chunkInfo] of Object.entries(bundle)) {
159
166
  if (chunkInfo.type === 'chunk' && fileName.includes(PLUGIN_CONSTANTS.SCRIPT_ID)) {
160
167
  delete bundle[fileName];
@@ -246,7 +253,7 @@ function csfRenderPlugin(componentData, _documentationComponents, handoff) {
246
253
  for (const [storyKey, storyValue] of stories) {
247
254
  const preview = componentData.previews[storyKey];
248
255
  const rendered = safeRenderToHtml(meta, storyValue, preview.values || {});
249
- const html = yield (0, html_1.formatHtml)(createHtmlDocument(componentId, preview.title, rendered));
256
+ const html = yield (0, html_1.formatHtml)(createHtmlDocument(componentId, preview.title, rendered, (_g = (_f = componentData.options) === null || _f === void 0 ? void 0 : _f.preview) === null || _g === void 0 ? void 0 : _g.css));
250
257
  const fileName = `${componentId}-${storyKey}.html`;
251
258
  this.emitFile({
252
259
  type: 'asset',
@@ -262,7 +269,7 @@ function csfRenderPlugin(componentData, _documentationComponents, handoff) {
262
269
  lastHtml = html;
263
270
  }
264
271
  // Emit standalone client module for Playground live rendering
265
- const firstStoryKey = (_f = stories[0]) === null || _f === void 0 ? void 0 : _f[0];
272
+ const firstStoryKey = (_h = stories[0]) === null || _h === void 0 ? void 0 : _h[0];
266
273
  if (firstStoryKey) {
267
274
  try {
268
275
  const clientModuleSource = (0, build_1.generateCsfClientComponentSource)(templatePath, firstStoryKey);
@@ -63,6 +63,7 @@ function processComponentInstances(componentData, documentationComponents) {
63
63
  */
64
64
  function renderHandlebarsTemplate(template, componentData, previewData, injectFieldWrappers) {
65
65
  return __awaiter(this, void 0, void 0, function* () {
66
+ var _a, _b;
66
67
  // Register Handlebars helpers with current injection state
67
68
  (0, handlebars_2.registerHandlebarsHelpers)({ id: componentData.id, properties: componentData.properties || {} }, injectFieldWrappers);
68
69
  const context = (0, handlebars_2.createHandlebarsContext)({
@@ -71,6 +72,7 @@ function renderHandlebarsTemplate(template, componentData, previewData, injectFi
71
72
  title: componentData.title
72
73
  }, previewData, {
73
74
  includeSharedStyles: Boolean(componentData.sharedStyles),
75
+ previewCss: (_b = (_a = componentData.options) === null || _a === void 0 ? void 0 : _a.preview) === null || _b === void 0 ? void 0 : _b.css,
74
76
  });
75
77
  const compiled = handlebars_1.default.compile(template)(context);
76
78
  return yield (0, html_1.formatHtmlWithWrapper)(compiled);
@@ -37,6 +37,7 @@ declare const buildComponentCss: (data: TransformComponentTokensResult, handoff:
37
37
  options?: {
38
38
  preview?: {
39
39
  groupBy?: string;
40
+ css?: string;
40
41
  };
41
42
  };
42
43
  validations?: Record<string, import("../../../types/preview").ValidationResult>;
@@ -37,6 +37,10 @@ export interface SlotMetadata {
37
37
  properties?: {
38
38
  [key: string]: SlotMetadata;
39
39
  };
40
+ options?: Array<string | {
41
+ value: string;
42
+ label: string;
43
+ }>;
40
44
  key?: string;
41
45
  rules?: RuleObject;
42
46
  docgenType?: string;