@squiz/dxp-cli-next 5.31.0-develop.2 → 5.31.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.
@@ -90,34 +90,8 @@ const createDeployCommand = () => {
90
90
  return deployCommand;
91
91
  };
92
92
  exports.default = createDeployCommand;
93
- /**
94
- * Formats backend error responses into user-friendly error messages.
95
- * Handles both validation errors (400s) and unknown server errors (500s).
96
- *
97
- * @param data - The error response data from the backend
98
- * @param statusCode - The HTTP status code (optional)
99
- * @returns Formatted error message string
100
- */
101
- function formatErrorResponse(data, statusCode) {
102
- // For 500 errors or unknown errors, show a generic message
103
- if (statusCode && statusCode >= 500) {
104
- return data.message || data.data || 'An unknown error occurred';
105
- }
106
- // For validation errors (400s), format with details
107
- let errorMessage = data.message || 'Validation failed';
108
- // Options validation errors come as an object with field keys
109
- if (data.details &&
110
- typeof data.details === 'object' &&
111
- !Array.isArray(data.details)) {
112
- const details = Object.entries(data.details)
113
- .map(([field, error]) => ` - ${field}: ${error.message}`)
114
- .join('\n');
115
- errorMessage += `\n${details}`;
116
- }
117
- return errorMessage;
118
- }
119
93
  function uploadLayout(client, layout, contentServiceUrl, dryRun) {
120
- var _a;
94
+ var _a, _b;
121
95
  return __awaiter(this, void 0, void 0, function* () {
122
96
  try {
123
97
  const queryParam = dryRun ? '?_dryRun=true' : '';
@@ -127,13 +101,13 @@ function uploadLayout(client, layout, contentServiceUrl, dryRun) {
127
101
  if (response.status === 200) {
128
102
  return response;
129
103
  }
130
- throw new Error(formatErrorResponse(response.data, response.status));
104
+ const error = new Error(response.data.message);
105
+ if ((_b = (_a = response.data.details) === null || _a === void 0 ? void 0 : _a.input) === null || _b === void 0 ? void 0 : _b.message) {
106
+ error.message += `: ${response.data.details.input.message}`;
107
+ }
108
+ throw error;
131
109
  }
132
110
  catch (error) {
133
- // Extract error details from axios error response
134
- if ((_a = error.response) === null || _a === void 0 ? void 0 : _a.data) {
135
- throw new Error(formatErrorResponse(error.response.data, error.response.status));
136
- }
137
111
  throw error;
138
112
  }
139
113
  });
@@ -170,7 +170,7 @@ describe('deployCommand', () => {
170
170
  const program = (0, deploy_1.default)();
171
171
  errorSpy = jest.spyOn(program, 'error').mockImplementation();
172
172
  yield program.parseAsync(createMockArgs({ contentServiceUrl }));
173
- expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Internal server error'));
173
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('An unknown error occurred'));
174
174
  }));
175
175
  it('should log additional details when layout validation fails', () => __awaiter(void 0, void 0, void 0, function* () {
176
176
  const dxpBaseUrl = 'http://dxp-base-url.com';
@@ -187,61 +187,8 @@ describe('deployCommand', () => {
187
187
  const program = (0, deploy_1.default)();
188
188
  errorSpy = jest.spyOn(program, 'error').mockImplementation();
189
189
  yield program.parseAsync(createMockArgs({ dxpBaseUrl }));
190
- const errorCall = errorSpy.mock.calls[0][0];
191
- expect(errorCall).toContain('- input: ERROR: Validation failed: "version" is an excess property and therefore is not allowed');
190
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Layout validation failed: ERROR: Validation failed: "version" is an excess property and therefore is not allowed'));
192
191
  }));
193
- describe('backend option validation error reporting', () => {
194
- it('should display clean error when single option validation fails', () => __awaiter(void 0, void 0, void 0, function* () {
195
- const contentServiceUrl = 'http://localhost:9999';
196
- (0, nock_1.default)(contentServiceUrl)
197
- .post('/page-layout')
198
- .reply(400, {
199
- message: 'Validation failed',
200
- details: {
201
- 'input.options.myBoolean': {
202
- message: "Option 'myBoolean' with valueType 'boolean' can not have values specified",
203
- },
204
- },
205
- });
206
- const program = (0, deploy_1.default)();
207
- errorSpy = jest.spyOn(program, 'error').mockImplementation();
208
- yield program.parseAsync(createMockArgs({ contentServiceUrl }));
209
- const errorCall = errorSpy.mock.calls[0][0];
210
- expect(errorCall).toContain('Validation failed');
211
- expect(errorCall).toContain("- input.options.myBoolean: Option 'myBoolean' with valueType 'boolean' can not have values specified");
212
- }));
213
- it('should display multiple option validation errors cleanly', () => __awaiter(void 0, void 0, void 0, function* () {
214
- const contentServiceUrl = 'http://localhost:9999';
215
- (0, nock_1.default)(contentServiceUrl)
216
- .post('/page-layout')
217
- .reply(400, {
218
- message: 'Validation failed',
219
- details: {
220
- 'input.options.badBoolean': {
221
- message: "Option 'badBoolean' with valueType 'boolean' can not have values specified",
222
- },
223
- 'input.options.badText': {
224
- message: "Option 'badText' with valueType 'text' can not have values specified",
225
- },
226
- 'input.options.emptyEnum': {
227
- message: "Option 'emptyEnum' with valueType 'string-enum' must have at least one value defined",
228
- },
229
- 'input.options.badOption.valueType': {
230
- message: "Option 'badOption' has invalid valueType 'INVALID'. Must be one of: string-enum, boolean, text",
231
- },
232
- },
233
- });
234
- const program = (0, deploy_1.default)();
235
- errorSpy = jest.spyOn(program, 'error').mockImplementation();
236
- yield program.parseAsync(createMockArgs({ contentServiceUrl }));
237
- const errorCall = errorSpy.mock.calls[0][0];
238
- expect(errorCall).toContain('Validation failed');
239
- expect(errorCall).toContain("- input.options.badBoolean: Option 'badBoolean' with valueType 'boolean' can not have values specified");
240
- expect(errorCall).toContain("- input.options.badText: Option 'badText' with valueType 'text' can not have values specified");
241
- expect(errorCall).toContain("- input.options.emptyEnum: Option 'emptyEnum' with valueType 'string-enum' must have at least one value defined");
242
- expect(errorCall).toContain("- input.options.badOption.valueType: Option 'badOption' has invalid valueType 'INVALID'. Must be one of: string-enum, boolean, text");
243
- }));
244
- });
245
192
  describe('zone consistency validation', () => {
246
193
  it('should handle zone consistency validation errors where zones are used but not defined in the layout', () => __awaiter(void 0, void 0, void 0, function* () {
247
194
  const file = './src/__tests__/layout.yaml';
@@ -45,14 +45,12 @@ const createDevCommand = () => {
45
45
  }
46
46
  // Confirm for entry property
47
47
  const layoutDefinition = Object.assign({}, rawLayoutDefinition);
48
- // Normalize options to convert string booleans to actual booleans
49
- const normalizedOptions = (0, parse_args_1.normalizeLayoutOptions)(options.options, layoutDefinition);
50
48
  exports.logger.info('Starting development server...');
51
49
  yield (0, server_1.startDevServer)({
52
50
  configPath: path_1.default.resolve(options.config),
53
51
  layoutDefinition,
54
52
  zoneContent: options.zones,
55
- layoutOptions: normalizedOptions,
53
+ layoutOptions: options.options,
56
54
  stylesheet: options.stylesheet
57
55
  ? path_1.default.resolve(options.stylesheet)
58
56
  : undefined,
@@ -136,33 +136,6 @@ describe('devCommand', () => {
136
136
  layoutOptions,
137
137
  }));
138
138
  }));
139
- it('normalizes boolean options from strings to actual booleans', () => __awaiter(void 0, void 0, void 0, function* () {
140
- const mockLayout = {
141
- name: 'Test Layout',
142
- options: {
143
- showFooter: { valueType: 'boolean' },
144
- showHeader: { valueType: 'boolean' },
145
- theme: { valueType: 'string-enum' },
146
- },
147
- };
148
- definitions_1.loadLayoutDefinition.mockResolvedValue(mockLayout);
149
- const layoutOptions = {
150
- showFooter: 'true',
151
- showHeader: 'false',
152
- theme: 'dark',
153
- };
154
- const program = (0, dev_1.default)();
155
- const args = createMockArgs({ layoutOptions });
156
- process.argv = args;
157
- yield program.parseAsync(args);
158
- expect(server_1.startDevServer).toHaveBeenCalledWith(expect.objectContaining({
159
- layoutOptions: {
160
- showFooter: true,
161
- showHeader: false,
162
- theme: 'dark', // Remains string
163
- },
164
- }));
165
- }));
166
139
  it('handles failure to load layout definition', () => __awaiter(void 0, void 0, void 0, function* () {
167
140
  const config = './src/__tests__/layout.yaml';
168
141
  definitions_1.loadLayoutDefinition.mockResolvedValue(undefined);
@@ -49,18 +49,15 @@ export declare const BaseLayoutDefinition: z.ZodObject<{
49
49
  options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
50
50
  displayName: z.ZodString;
51
51
  description: z.ZodString;
52
- valueType: z.ZodOptional<z.ZodEnum<["string-enum", "boolean", "text"]>>;
53
- values: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
52
+ values: z.ZodArray<z.ZodString, "many">;
54
53
  }, "strip", z.ZodTypeAny, {
54
+ values: string[];
55
55
  description: string;
56
56
  displayName: string;
57
- values?: string[] | undefined;
58
- valueType?: "boolean" | "text" | "string-enum" | undefined;
59
57
  }, {
58
+ values: string[];
60
59
  description: string;
61
60
  displayName: string;
62
- values?: string[] | undefined;
63
- valueType?: "boolean" | "text" | "string-enum" | undefined;
64
61
  }>>>;
65
62
  }, "strip", z.ZodTypeAny, {
66
63
  name: string;
@@ -73,10 +70,9 @@ export declare const BaseLayoutDefinition: z.ZodObject<{
73
70
  maxNodes?: number | undefined;
74
71
  }>;
75
72
  options?: Record<string, {
73
+ values: string[];
76
74
  description: string;
77
75
  displayName: string;
78
- values?: string[] | undefined;
79
- valueType?: "boolean" | "text" | "string-enum" | undefined;
80
76
  }> | undefined;
81
77
  }, {
82
78
  name: string;
@@ -89,10 +85,9 @@ export declare const BaseLayoutDefinition: z.ZodObject<{
89
85
  maxNodes?: number | undefined;
90
86
  }>;
91
87
  options?: Record<string, {
88
+ values: string[];
92
89
  description: string;
93
90
  displayName: string;
94
- values?: string[] | undefined;
95
- valueType?: "boolean" | "text" | "string-enum" | undefined;
96
91
  }> | undefined;
97
92
  }>;
98
93
  export declare const InputLayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
@@ -138,18 +133,15 @@ export declare const InputLayoutDefinition: z.ZodObject<z.objectUtil.extendShape
138
133
  options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
139
134
  displayName: z.ZodString;
140
135
  description: z.ZodString;
141
- valueType: z.ZodOptional<z.ZodEnum<["string-enum", "boolean", "text"]>>;
142
- values: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
136
+ values: z.ZodArray<z.ZodString, "many">;
143
137
  }, "strip", z.ZodTypeAny, {
138
+ values: string[];
144
139
  description: string;
145
140
  displayName: string;
146
- values?: string[] | undefined;
147
- valueType?: "boolean" | "text" | "string-enum" | undefined;
148
141
  }, {
142
+ values: string[];
149
143
  description: string;
150
144
  displayName: string;
151
- values?: string[] | undefined;
152
- valueType?: "boolean" | "text" | "string-enum" | undefined;
153
145
  }>>>;
154
146
  }, {
155
147
  entry: z.ZodString;
@@ -165,10 +157,9 @@ export declare const InputLayoutDefinition: z.ZodObject<z.objectUtil.extendShape
165
157
  maxNodes?: number | undefined;
166
158
  }>;
167
159
  options?: Record<string, {
160
+ values: string[];
168
161
  description: string;
169
162
  displayName: string;
170
- values?: string[] | undefined;
171
- valueType?: "boolean" | "text" | "string-enum" | undefined;
172
163
  }> | undefined;
173
164
  }, {
174
165
  name: string;
@@ -182,10 +173,9 @@ export declare const InputLayoutDefinition: z.ZodObject<z.objectUtil.extendShape
182
173
  maxNodes?: number | undefined;
183
174
  }>;
184
175
  options?: Record<string, {
176
+ values: string[];
185
177
  description: string;
186
178
  displayName: string;
187
- values?: string[] | undefined;
188
- valueType?: "boolean" | "text" | "string-enum" | undefined;
189
179
  }> | undefined;
190
180
  }>;
191
181
  export declare const LayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
@@ -231,18 +221,15 @@ export declare const LayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
231
221
  options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
232
222
  displayName: z.ZodString;
233
223
  description: z.ZodString;
234
- valueType: z.ZodOptional<z.ZodEnum<["string-enum", "boolean", "text"]>>;
235
- values: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
224
+ values: z.ZodArray<z.ZodString, "many">;
236
225
  }, "strip", z.ZodTypeAny, {
226
+ values: string[];
237
227
  description: string;
238
228
  displayName: string;
239
- values?: string[] | undefined;
240
- valueType?: "boolean" | "text" | "string-enum" | undefined;
241
229
  }, {
230
+ values: string[];
242
231
  description: string;
243
232
  displayName: string;
244
- values?: string[] | undefined;
245
- valueType?: "boolean" | "text" | "string-enum" | undefined;
246
233
  }>>>;
247
234
  }, {
248
235
  template: z.ZodString;
@@ -258,10 +245,9 @@ export declare const LayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
258
245
  }>;
259
246
  template: string;
260
247
  options?: Record<string, {
248
+ values: string[];
261
249
  description: string;
262
250
  displayName: string;
263
- values?: string[] | undefined;
264
- valueType?: "boolean" | "text" | "string-enum" | undefined;
265
251
  }> | undefined;
266
252
  }, {
267
253
  name: string;
@@ -275,10 +261,9 @@ export declare const LayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
275
261
  }>;
276
262
  template: string;
277
263
  options?: Record<string, {
264
+ values: string[];
278
265
  description: string;
279
266
  displayName: string;
280
- values?: string[] | undefined;
281
- valueType?: "boolean" | "text" | "string-enum" | undefined;
282
267
  }> | undefined;
283
268
  }>;
284
269
  export declare type InputLayoutDefinition = z.infer<typeof InputLayoutDefinition>;
@@ -56,7 +56,6 @@ const zod_1 = require("zod");
56
56
  const yaml_1 = require("yaml");
57
57
  // Local
58
58
  const validation_1 = require("../templates/validation");
59
- const render_1 = require("./render");
60
59
  function loadLayoutDefinition(layoutFile) {
61
60
  return __awaiter(this, void 0, void 0, function* () {
62
61
  try {
@@ -150,8 +149,7 @@ exports.BaseLayoutDefinition = zod_1.z.object({
150
149
  .record(zod_1.z.string(), zod_1.z.object({
151
150
  displayName: zod_1.z.string(),
152
151
  description: zod_1.z.string(),
153
- valueType: zod_1.z.enum(render_1.LayoutOptionValueTypes).optional(),
154
- values: zod_1.z.array(zod_1.z.string()).optional(),
152
+ values: zod_1.z.array(zod_1.z.string()),
155
153
  }))
156
154
  .optional(),
157
155
  });
@@ -88,16 +88,6 @@ entry: template.hbs
88
88
  description: 'Color options',
89
89
  values: ['red', 'blue'],
90
90
  },
91
- showHeader: {
92
- displayName: 'Show Header',
93
- description: 'Toggle header visibility',
94
- valueType: 'boolean',
95
- },
96
- customTitle: {
97
- displayName: 'Custom Title',
98
- description: 'Enter a custom title',
99
- valueType: 'text',
100
- },
101
91
  },
102
92
  entry: 'template.hbs',
103
93
  });
@@ -188,16 +178,6 @@ entry: template.hbs
188
178
  description: 'Color options',
189
179
  values: ['red', 'blue'],
190
180
  },
191
- showHeader: {
192
- displayName: 'Show Header',
193
- description: 'Toggle header visibility',
194
- valueType: 'boolean',
195
- },
196
- customTitle: {
197
- displayName: 'Custom Title',
198
- description: 'Enter a custom title',
199
- valueType: 'text',
200
- },
201
181
  },
202
182
  template: templateContent,
203
183
  });
@@ -456,114 +436,6 @@ describe('LayoutDefinitionParse', () => {
456
436
  });
457
437
  expect(layoutDefinition.zones.main.minNodes).toBe(0);
458
438
  });
459
- describe('option schema validation', () => {
460
- it('should accept specified valueTypes', () => {
461
- var _a, _b, _c;
462
- const layoutDefinition = definitions_1.BaseLayoutDefinition.parse({
463
- name: 'test-layout',
464
- displayName: 'Test Layout',
465
- description: 'A test layout',
466
- zones: {
467
- main: {
468
- displayName: 'Main Zone',
469
- description: 'Main content area',
470
- },
471
- },
472
- options: {
473
- theme: {
474
- displayName: 'Theme',
475
- description: 'Color theme',
476
- valueType: 'string-enum',
477
- values: ['light', 'dark'],
478
- },
479
- showSidebar: {
480
- displayName: 'Show Sidebar',
481
- description: 'Toggle sidebar',
482
- valueType: 'boolean',
483
- },
484
- customCss: {
485
- displayName: 'Custom CSS',
486
- description: 'Enter custom CSS',
487
- valueType: 'text',
488
- },
489
- },
490
- });
491
- expect((_a = layoutDefinition.options) === null || _a === void 0 ? void 0 : _a.theme.valueType).toBe('string-enum');
492
- expect((_b = layoutDefinition.options) === null || _b === void 0 ? void 0 : _b.showSidebar.valueType).toBe('boolean');
493
- expect((_c = layoutDefinition.options) === null || _c === void 0 ? void 0 : _c.customCss.valueType).toBe('text');
494
- });
495
- it('should reject invalid valueType', () => {
496
- expect(() => definitions_1.BaseLayoutDefinition.parse({
497
- name: 'test-layout',
498
- displayName: 'Test Layout',
499
- description: 'A test layout',
500
- zones: {
501
- main: {
502
- displayName: 'Main Zone',
503
- description: 'Main content area',
504
- },
505
- },
506
- options: {
507
- badOption: {
508
- displayName: 'Bad Option',
509
- description: 'Invalid valueType',
510
- valueType: 'INVALID_TYPE',
511
- },
512
- },
513
- })).toThrow();
514
- });
515
- it('should allow valueType and values to be optional', () => {
516
- var _a, _b, _c, _d, _e, _f, _g, _h;
517
- const layoutDefinition = definitions_1.BaseLayoutDefinition.parse({
518
- name: 'test-layout',
519
- displayName: 'Test Layout',
520
- description: 'A test layout',
521
- zones: {
522
- main: {
523
- displayName: 'Main Zone',
524
- description: 'Main content area',
525
- },
526
- },
527
- options: {
528
- withBoth: {
529
- displayName: 'With Both',
530
- description: 'Has valueType and values',
531
- valueType: 'string-enum',
532
- values: ['option1', 'option2'],
533
- },
534
- withValueTypeOnly: {
535
- displayName: 'With ValueType Only',
536
- description: 'Has valueType but no values',
537
- valueType: 'boolean',
538
- },
539
- withValuesOnly: {
540
- displayName: 'With Values Only',
541
- description: 'Has values but no valueType (legacy)',
542
- values: ['legacy1', 'legacy2'],
543
- },
544
- withNeither: {
545
- displayName: 'With Neither',
546
- description: 'Has neither valueType nor values',
547
- },
548
- },
549
- });
550
- // Schema accepts all combinations - Business logic validation (e.g., boolean/text can't have values) happens on backend and reported in CLI on deploy.
551
- expect((_a = layoutDefinition.options) === null || _a === void 0 ? void 0 : _a.withBoth.valueType).toBe('string-enum');
552
- expect((_b = layoutDefinition.options) === null || _b === void 0 ? void 0 : _b.withBoth.values).toEqual([
553
- 'option1',
554
- 'option2',
555
- ]);
556
- expect((_c = layoutDefinition.options) === null || _c === void 0 ? void 0 : _c.withValueTypeOnly.valueType).toBe('boolean');
557
- expect((_d = layoutDefinition.options) === null || _d === void 0 ? void 0 : _d.withValueTypeOnly.values).toBeUndefined();
558
- expect((_e = layoutDefinition.options) === null || _e === void 0 ? void 0 : _e.withValuesOnly.valueType).toBeUndefined();
559
- expect((_f = layoutDefinition.options) === null || _f === void 0 ? void 0 : _f.withValuesOnly.values).toEqual([
560
- 'legacy1',
561
- 'legacy2',
562
- ]);
563
- expect((_g = layoutDefinition.options) === null || _g === void 0 ? void 0 : _g.withNeither.valueType).toBeUndefined();
564
- expect((_h = layoutDefinition.options) === null || _h === void 0 ? void 0 : _h.withNeither.values).toBeUndefined();
565
- });
566
- });
567
439
  });
568
440
  describe('validateTemplateFile', () => {
569
441
  describe('valid templates', () => {
@@ -19,26 +19,10 @@ export declare function parseZonesList(value: string, previous: Record<string, s
19
19
  * - Comma-separated list: 'key1=value1,key2=value2' or 'key1:value1,key2:value2'
20
20
  * - Multiple --options flags: later values override earlier ones
21
21
  *
22
- * Note: All values are returned as strings. Use normalizeLayoutOptions() to convert
23
- * boolean options to actual boolean types based on the layout definition.
24
- *
25
22
  * @param value Current value being processed
26
23
  * @param previous Previously processed value (used for multiple flags)
27
- * @returns Record of option names to string values
24
+ * @returns Record of option names to values
28
25
  */
29
26
  export declare function parseOptionsList(value: string, previous: Record<string, string>): {
30
27
  [x: string]: string;
31
28
  };
32
- /**
33
- * Normalizes layout options by converting string boolean values to actual booleans
34
- * based on the layout definition's option types.
35
- *
36
- * @param options The parsed options from CLI (all strings)
37
- * @param layoutDefinition The layout definition with option type information
38
- * @returns Options with boolean values converted to actual booleans
39
- */
40
- export declare function normalizeLayoutOptions(options: Record<string, string>, layoutDefinition: {
41
- options?: Record<string, {
42
- valueType?: string;
43
- }>;
44
- }): Record<string, string | boolean>;
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalizeLayoutOptions = exports.parseOptionsList = exports.parseZonesList = void 0;
4
- const render_1 = require("./render");
3
+ exports.parseOptionsList = exports.parseZonesList = void 0;
5
4
  /**
6
5
  * Parse zones list from command line
7
6
  * Supports multiple formats:
@@ -51,12 +50,9 @@ exports.parseZonesList = parseZonesList;
51
50
  * - Comma-separated list: 'key1=value1,key2=value2' or 'key1:value1,key2:value2'
52
51
  * - Multiple --options flags: later values override earlier ones
53
52
  *
54
- * Note: All values are returned as strings. Use normalizeLayoutOptions() to convert
55
- * boolean options to actual boolean types based on the layout definition.
56
- *
57
53
  * @param value Current value being processed
58
54
  * @param previous Previously processed value (used for multiple flags)
59
- * @returns Record of option names to string values
55
+ * @returns Record of option names to values
60
56
  */
61
57
  function parseOptionsList(value, previous) {
62
58
  const result = Object.assign({}, previous);
@@ -86,35 +82,3 @@ function parseOptionsList(value, previous) {
86
82
  return result;
87
83
  }
88
84
  exports.parseOptionsList = parseOptionsList;
89
- /**
90
- * Normalizes layout options by converting string boolean values to actual booleans
91
- * based on the layout definition's option types.
92
- *
93
- * @param options The parsed options from CLI (all strings)
94
- * @param layoutDefinition The layout definition with option type information
95
- * @returns Options with boolean values converted to actual booleans
96
- */
97
- function normalizeLayoutOptions(options, layoutDefinition) {
98
- const normalized = Object.assign({}, options);
99
- if (!layoutDefinition.options) {
100
- return normalized;
101
- }
102
- // Convert string "true"/"false" to actual booleans for boolean-type options
103
- for (const [key, optionDef] of Object.entries(layoutDefinition.options)) {
104
- if (optionDef.valueType === render_1.LayoutOptionValueType.boolean &&
105
- key in normalized) {
106
- const value = normalized[key];
107
- if (value === 'true') {
108
- normalized[key] = true;
109
- }
110
- else if (value === 'false') {
111
- normalized[key] = false;
112
- }
113
- else {
114
- throw new Error(`Invalid boolean value "${value}" for option "${key}". Must be "true" or "false".`);
115
- }
116
- }
117
- }
118
- return normalized;
119
- }
120
- exports.normalizeLayoutOptions = normalizeLayoutOptions;
@@ -144,141 +144,3 @@ describe('parseOptionsList', () => {
144
144
  });
145
145
  });
146
146
  });
147
- describe('normalizeLayoutOptions', () => {
148
- it('returns empty object when given no options', () => {
149
- const result = (0, parse_args_1.normalizeLayoutOptions)({}, {});
150
- expect(result).toEqual({});
151
- });
152
- it('returns options unchanged when layoutDefinition has no options', () => {
153
- const options = {
154
- theme: 'dark',
155
- size: 'large',
156
- };
157
- const result = (0, parse_args_1.normalizeLayoutOptions)(options, {});
158
- expect(result).toEqual(options);
159
- });
160
- it('converts string "true" to boolean true for boolean-type options', () => {
161
- const options = {
162
- showFooter: 'true',
163
- };
164
- const layoutDefinition = {
165
- options: {
166
- showFooter: {
167
- valueType: 'boolean',
168
- },
169
- },
170
- };
171
- const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
172
- expect(result).toEqual({
173
- showFooter: true,
174
- });
175
- expect(typeof result.showFooter).toBe('boolean');
176
- });
177
- it('converts string "false" to boolean false for boolean-type options', () => {
178
- const options = {
179
- showFooter: 'false',
180
- };
181
- const layoutDefinition = {
182
- options: {
183
- showFooter: {
184
- valueType: 'boolean',
185
- },
186
- },
187
- };
188
- const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
189
- expect(result).toEqual({
190
- showFooter: false,
191
- });
192
- expect(typeof result.showFooter).toBe('boolean');
193
- });
194
- it('leaves string options unchanged for non-boolean types', () => {
195
- const options = {
196
- theme: 'dark',
197
- customTitle: 'My Title',
198
- };
199
- const layoutDefinition = {
200
- options: {
201
- theme: {
202
- valueType: 'string-enum',
203
- },
204
- customTitle: {
205
- valueType: 'text',
206
- },
207
- },
208
- };
209
- const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
210
- expect(result).toEqual(options);
211
- });
212
- it('handles mixed boolean and string options', () => {
213
- const options = {
214
- showFooter: 'true',
215
- showHeader: 'false',
216
- theme: 'dark',
217
- customTitle: 'Test',
218
- };
219
- const layoutDefinition = {
220
- options: {
221
- showFooter: { valueType: 'boolean' },
222
- showHeader: { valueType: 'boolean' },
223
- theme: { valueType: 'string-enum' },
224
- customTitle: { valueType: 'text' },
225
- },
226
- };
227
- const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
228
- expect(result).toEqual({
229
- showFooter: true,
230
- showHeader: false,
231
- theme: 'dark',
232
- customTitle: 'Test',
233
- });
234
- });
235
- it('throws error for invalid boolean value', () => {
236
- const options = {
237
- showFooter: 'yes',
238
- };
239
- const layoutDefinition = {
240
- options: {
241
- showFooter: {
242
- valueType: 'boolean',
243
- },
244
- },
245
- };
246
- expect(() => (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition)).toThrow('Invalid boolean value "yes" for option "showFooter". Must be "true" or "false".');
247
- });
248
- it('only normalizes options that exist in layoutDefinition', () => {
249
- const options = {
250
- showFooter: 'true',
251
- unknownOption: 'true',
252
- };
253
- const layoutDefinition = {
254
- options: {
255
- showFooter: {
256
- valueType: 'boolean',
257
- },
258
- },
259
- };
260
- const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
261
- expect(result).toEqual({
262
- showFooter: true,
263
- unknownOption: 'true', // Left as string because not in layoutDefinition
264
- });
265
- });
266
- it('preserves options not present in layoutDefinition options', () => {
267
- const options = {
268
- showFooter: 'true',
269
- theme: 'dark',
270
- };
271
- const layoutDefinition = {
272
- options: {
273
- showFooter: {
274
- valueType: 'boolean',
275
- },
276
- },
277
- };
278
- const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
279
- expect(result).toEqual({
280
- showFooter: true,
281
- theme: 'dark', // Preserved even though not in layoutDefinition
282
- });
283
- });
284
- });
@@ -16,29 +16,18 @@ export interface ZoneDefinition {
16
16
  minNodes?: number;
17
17
  maxNodes?: number;
18
18
  }
19
- /**
20
- * Layout option value types.
21
- */
22
- export declare const LayoutOptionValueType: {
23
- readonly stringEnum: "string-enum";
24
- readonly boolean: "boolean";
25
- readonly text: "text";
26
- };
27
- export declare type LayoutOptionValueType = typeof LayoutOptionValueType[keyof typeof LayoutOptionValueType];
28
- export declare const LayoutOptionValueTypes: readonly ["string-enum", "boolean", "text"];
29
19
  export interface OptionDefinition {
30
20
  displayName: string;
31
21
  description: string;
32
- valueType?: LayoutOptionValueType;
33
- values?: string[];
22
+ values: string[];
34
23
  }
35
24
  /**
36
25
  * Renders a layout using Handlebars templating
37
26
  *
38
27
  * @param templateContent The Handlebars template string
39
28
  * @param zoneContents Content for each zone as single concatenated strings
40
- * @param layoutOptions Options for the layout (can include boolean values for boolean-type options)
29
+ * @param layoutOptions Options for the layout
41
30
  * @param layoutDefinition Optional layout definition object to pass to the template
42
31
  * @returns The rendered HTML
43
32
  */
44
- export declare function renderLayout(templateContent: string, zoneContents: Record<string, string>, layoutOptions: Record<string, string | boolean>, layoutDefinition?: ExtendedLayoutDefinition): Promise<string>;
33
+ export declare function renderLayout(templateContent: string, zoneContents: Record<string, string>, layoutOptions: Record<string, any>, layoutDefinition?: ExtendedLayoutDefinition): Promise<string>;
@@ -12,27 +12,14 @@ 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.renderLayout = exports.LayoutOptionValueTypes = exports.LayoutOptionValueType = void 0;
15
+ exports.renderLayout = void 0;
16
16
  const handlebars_1 = __importDefault(require("handlebars"));
17
- /**
18
- * Layout option value types.
19
- */
20
- exports.LayoutOptionValueType = {
21
- stringEnum: 'string-enum',
22
- boolean: 'boolean',
23
- text: 'text',
24
- };
25
- exports.LayoutOptionValueTypes = [
26
- exports.LayoutOptionValueType.stringEnum,
27
- exports.LayoutOptionValueType.boolean,
28
- exports.LayoutOptionValueType.text,
29
- ];
30
17
  /**
31
18
  * Renders a layout using Handlebars templating
32
19
  *
33
20
  * @param templateContent The Handlebars template string
34
21
  * @param zoneContents Content for each zone as single concatenated strings
35
- * @param layoutOptions Options for the layout (can include boolean values for boolean-type options)
22
+ * @param layoutOptions Options for the layout
36
23
  * @param layoutDefinition Optional layout definition object to pass to the template
37
24
  * @returns The rendered HTML
38
25
  */
@@ -68,7 +68,7 @@ describe('renderLayout', () => {
68
68
  main: '<p>Content</p>',
69
69
  };
70
70
  const layoutOptions = {
71
- theme: 'dark',
71
+ theme: { selectedValue: 'dark' },
72
72
  };
73
73
  const result = yield (0, render_1.renderLayout)(templateContent, zoneContents, layoutOptions);
74
74
  expect(result).toContain('<div class="theme-dark">');
@@ -3,7 +3,7 @@ interface DevServerOptions {
3
3
  configPath: string;
4
4
  layoutDefinition: ExtendedLayoutDefinition;
5
5
  zoneContent: Record<string, string[]>;
6
- layoutOptions: Record<string, string | boolean>;
6
+ layoutOptions: Record<string, string>;
7
7
  stylesheet?: string;
8
8
  port: number;
9
9
  openBrowser: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/dxp-cli-next",
3
- "version": "5.31.0-develop.2",
3
+ "version": "5.31.0",
4
4
  "repository": {
5
5
  "url": "https://gitlab.squiz.net/dxp/dxp-cli-next"
6
6
  },