@squiz/dxp-cli-next 5.31.0-develop.4 → 5.31.0-develop.5

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 (34) hide show
  1. package/lib/page/layouts/deploy/deploy.js +43 -8
  2. package/lib/page/layouts/deploy/deploy.spec.js +110 -19
  3. package/lib/page/layouts/dev/dev.js +7 -3
  4. package/lib/page/layouts/dev/dev.spec.js +35 -8
  5. package/lib/page/layouts/validation/index.d.ts +2 -0
  6. package/lib/page/layouts/validation/index.js +5 -1
  7. package/lib/page/layouts/validation/property-consistency.d.ts +7 -0
  8. package/lib/page/layouts/validation/property-consistency.js +92 -0
  9. package/lib/page/layouts/validation/property-consistency.spec.d.ts +1 -0
  10. package/lib/page/layouts/validation/property-consistency.spec.js +305 -0
  11. package/lib/page/layouts/validation/validateLayoutFormat.d.ts +2 -0
  12. package/lib/page/layouts/validation/validateLayoutFormat.js +25 -0
  13. package/lib/page/layouts/validation/validateLayoutFormat.spec.d.ts +1 -0
  14. package/lib/page/layouts/validation/validateLayoutFormat.spec.js +40 -0
  15. package/lib/page/layouts/validation/zone-consistency.d.ts +1 -1
  16. package/lib/page/layouts/validation/zone-consistency.js +10 -9
  17. package/lib/page/layouts/validation/zone-consistency.spec.js +32 -34
  18. package/lib/page/utils/definitions.d.ts +346 -49
  19. package/lib/page/utils/definitions.js +102 -21
  20. package/lib/page/utils/definitions.spec.js +460 -267
  21. package/lib/page/utils/normalize.d.ts +8 -0
  22. package/lib/page/utils/normalize.js +61 -0
  23. package/lib/page/utils/normalize.spec.d.ts +1 -0
  24. package/lib/page/utils/normalize.spec.js +315 -0
  25. package/lib/page/utils/parse-args.d.ts +20 -4
  26. package/lib/page/utils/parse-args.js +48 -13
  27. package/lib/page/utils/parse-args.spec.js +159 -21
  28. package/lib/page/utils/render.d.ts +27 -9
  29. package/lib/page/utils/render.js +66 -12
  30. package/lib/page/utils/render.spec.js +14 -14
  31. package/lib/page/utils/server.d.ts +1 -1
  32. package/lib/page/utils/server.js +2 -2
  33. package/lib/page/utils/server.spec.js +13 -13
  34. package/package.json +1 -1
@@ -0,0 +1,8 @@
1
+ /*!
2
+ * @license
3
+ * Copyright (c) 2025, Squiz Australia Pty Ltd.
4
+ * All rights reserved.
5
+ */
6
+ import { InputLayoutDefinition, LayoutDefinition } from './definitions';
7
+ export declare function normalizeZones(zones: InputLayoutDefinition['zones']): LayoutDefinition['zones'];
8
+ export declare function normalizeProperties(layout: InputLayoutDefinition): LayoutDefinition['properties'];
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /*!
3
+ * @license
4
+ * Copyright (c) 2025, Squiz Australia Pty Ltd.
5
+ * All rights reserved.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.normalizeProperties = exports.normalizeZones = void 0;
9
+ const render_1 = require("./render");
10
+ function normalizeZones(zones) {
11
+ if (Array.isArray(zones)) {
12
+ return zones;
13
+ }
14
+ if (typeof zones !== 'object' || zones === null) {
15
+ throw new Error('zones must be an object or array');
16
+ }
17
+ return Object.entries(zones).map(([key, zone]) => ({
18
+ key,
19
+ displayName: zone.displayName,
20
+ description: zone.description,
21
+ }));
22
+ }
23
+ exports.normalizeZones = normalizeZones;
24
+ function normalizeProperties(layout) {
25
+ if ('properties' in layout) {
26
+ return layout.properties;
27
+ }
28
+ if (!('options' in layout) || layout.options === undefined) {
29
+ return undefined;
30
+ }
31
+ return Object.entries(layout.options).reduce((acc, [key, option]) => {
32
+ switch (option.valueType) {
33
+ case render_1.LayoutOptionValueType.boolean:
34
+ acc[key] = {
35
+ type: render_1.LayoutPropertyType.boolean,
36
+ title: option.displayName,
37
+ description: option.description,
38
+ };
39
+ break;
40
+ case render_1.LayoutOptionValueType.text:
41
+ acc[key] = {
42
+ type: render_1.LayoutPropertyType.string,
43
+ title: option.displayName,
44
+ description: option.description,
45
+ };
46
+ break;
47
+ case render_1.LayoutOptionValueType.stringEnum:
48
+ default:
49
+ acc[key] = {
50
+ type: render_1.LayoutPropertyType.string,
51
+ title: option.displayName,
52
+ description: option.description,
53
+ };
54
+ if (option.values !== undefined) {
55
+ acc[key].enum = option.values;
56
+ }
57
+ }
58
+ return acc;
59
+ }, {});
60
+ }
61
+ exports.normalizeProperties = normalizeProperties;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,315 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const normalize_1 = require("./normalize");
4
+ const render_1 = require("./render");
5
+ describe('normalizeZones', () => {
6
+ it('should return zones array as-is when zones is already an array (V2 format)', () => {
7
+ const zones = [
8
+ {
9
+ key: 'main',
10
+ displayName: 'Main Zone',
11
+ description: 'Main content area',
12
+ },
13
+ {
14
+ key: 'sidebar',
15
+ displayName: 'Sidebar Zone',
16
+ description: 'Sidebar content area',
17
+ },
18
+ ];
19
+ const result = (0, normalize_1.normalizeZones)(zones);
20
+ expect(result).toEqual(zones);
21
+ expect(result).toBe(zones); // Should return the same reference
22
+ });
23
+ it('should convert zones object to array format (V1 format)', () => {
24
+ const zones = {
25
+ main: {
26
+ displayName: 'Main Zone',
27
+ description: 'Main content area',
28
+ minNodes: 1,
29
+ maxNodes: 5,
30
+ },
31
+ sidebar: {
32
+ displayName: 'Sidebar Zone',
33
+ description: 'Sidebar content area',
34
+ minNodes: 0,
35
+ },
36
+ };
37
+ const result = (0, normalize_1.normalizeZones)(zones);
38
+ expect(result).toEqual([
39
+ {
40
+ key: 'main',
41
+ displayName: 'Main Zone',
42
+ description: 'Main content area',
43
+ },
44
+ {
45
+ key: 'sidebar',
46
+ displayName: 'Sidebar Zone',
47
+ description: 'Sidebar content area',
48
+ },
49
+ ]);
50
+ });
51
+ it('should handle empty zones object', () => {
52
+ const zones = {};
53
+ const result = (0, normalize_1.normalizeZones)(zones);
54
+ expect(result).toEqual([]);
55
+ });
56
+ it('should handle empty zones array', () => {
57
+ const zones = [];
58
+ const result = (0, normalize_1.normalizeZones)(zones);
59
+ expect(result).toEqual([]);
60
+ });
61
+ it('should throw error when zones is null', () => {
62
+ expect(() => (0, normalize_1.normalizeZones)(null)).toThrow('zones must be an object or array');
63
+ });
64
+ it('should throw error when zones is a string', () => {
65
+ expect(() => (0, normalize_1.normalizeZones)('invalid')).toThrow('zones must be an object or array');
66
+ });
67
+ it('should throw error when zones is a number', () => {
68
+ expect(() => (0, normalize_1.normalizeZones)(123)).toThrow('zones must be an object or array');
69
+ });
70
+ it('should throw error when zones is undefined', () => {
71
+ expect(() => (0, normalize_1.normalizeZones)(undefined)).toThrow('zones must be an object or array');
72
+ });
73
+ });
74
+ describe('normalizeProperties', () => {
75
+ describe('V2 format (properties)', () => {
76
+ it('should return properties as-is when layout has properties', () => {
77
+ const layout = {
78
+ name: 'test-layout',
79
+ displayName: 'Test Layout',
80
+ description: 'A test layout',
81
+ entry: 'template.hbs',
82
+ zones: [],
83
+ properties: {
84
+ title: {
85
+ type: 'string',
86
+ title: 'Title',
87
+ description: 'Page title',
88
+ },
89
+ showHeader: {
90
+ type: 'boolean',
91
+ title: 'Show Header',
92
+ description: 'Whether to show header',
93
+ },
94
+ },
95
+ };
96
+ const result = (0, normalize_1.normalizeProperties)(layout);
97
+ expect(result).toEqual(layout.properties);
98
+ expect(result).toBe(layout.properties); // Should return the same reference
99
+ });
100
+ it('should return undefined when layout has no properties', () => {
101
+ const layout = {
102
+ name: 'test-layout',
103
+ displayName: 'Test Layout',
104
+ description: 'A test layout',
105
+ entry: 'template.hbs',
106
+ zones: [],
107
+ };
108
+ const result = (0, normalize_1.normalizeProperties)(layout);
109
+ expect(result).toBeUndefined();
110
+ });
111
+ });
112
+ describe('V1 format (options)', () => {
113
+ it('should convert boolean option to boolean property', () => {
114
+ const layout = {
115
+ name: 'test-layout',
116
+ displayName: 'Test Layout',
117
+ description: 'A test layout',
118
+ entry: 'template.hbs',
119
+ zones: {},
120
+ options: {
121
+ showHeader: {
122
+ displayName: 'Show Header',
123
+ description: 'Whether to show header',
124
+ valueType: render_1.LayoutOptionValueType.boolean,
125
+ },
126
+ },
127
+ };
128
+ const result = (0, normalize_1.normalizeProperties)(layout);
129
+ expect(result).toEqual({
130
+ showHeader: {
131
+ type: 'boolean',
132
+ title: 'Show Header',
133
+ description: 'Whether to show header',
134
+ },
135
+ });
136
+ });
137
+ it('should convert text option to string property', () => {
138
+ const layout = {
139
+ name: 'test-layout',
140
+ displayName: 'Test Layout',
141
+ description: 'A test layout',
142
+ entry: 'template.hbs',
143
+ zones: {},
144
+ options: {
145
+ title: {
146
+ displayName: 'Title',
147
+ description: 'Page title',
148
+ valueType: render_1.LayoutOptionValueType.text,
149
+ },
150
+ },
151
+ };
152
+ const result = (0, normalize_1.normalizeProperties)(layout);
153
+ expect(result).toEqual({
154
+ title: {
155
+ type: 'string',
156
+ title: 'Title',
157
+ description: 'Page title',
158
+ },
159
+ });
160
+ });
161
+ it('should convert stringEnum option to string property with enum', () => {
162
+ const layout = {
163
+ name: 'test-layout',
164
+ displayName: 'Test Layout',
165
+ description: 'A test layout',
166
+ entry: 'template.hbs',
167
+ zones: {},
168
+ options: {
169
+ theme: {
170
+ displayName: 'Theme',
171
+ description: 'Color theme',
172
+ valueType: render_1.LayoutOptionValueType.stringEnum,
173
+ values: ['light', 'dark', 'auto'],
174
+ },
175
+ },
176
+ };
177
+ const result = (0, normalize_1.normalizeProperties)(layout);
178
+ expect(result).toEqual({
179
+ theme: {
180
+ type: 'string',
181
+ title: 'Theme',
182
+ description: 'Color theme',
183
+ enum: ['light', 'dark', 'auto'],
184
+ },
185
+ });
186
+ });
187
+ it('should convert stringEnum option without values to string property without enum', () => {
188
+ const layout = {
189
+ name: 'test-layout',
190
+ displayName: 'Test Layout',
191
+ description: 'A test layout',
192
+ entry: 'template.hbs',
193
+ zones: {},
194
+ options: {
195
+ theme: {
196
+ displayName: 'Theme',
197
+ description: 'Color theme',
198
+ valueType: render_1.LayoutOptionValueType.stringEnum,
199
+ },
200
+ },
201
+ };
202
+ const result = (0, normalize_1.normalizeProperties)(layout);
203
+ expect(result).toEqual({
204
+ theme: {
205
+ type: 'string',
206
+ title: 'Theme',
207
+ description: 'Color theme',
208
+ },
209
+ });
210
+ });
211
+ it('should default to string property when valueType is undefined', () => {
212
+ const layout = {
213
+ name: 'test-layout',
214
+ displayName: 'Test Layout',
215
+ description: 'A test layout',
216
+ entry: 'template.hbs',
217
+ zones: {},
218
+ options: {
219
+ title: {
220
+ displayName: 'Title',
221
+ description: 'Page title',
222
+ },
223
+ },
224
+ };
225
+ const result = (0, normalize_1.normalizeProperties)(layout);
226
+ expect(result).toEqual({
227
+ title: {
228
+ type: 'string',
229
+ title: 'Title',
230
+ description: 'Page title',
231
+ },
232
+ });
233
+ });
234
+ it('should convert multiple options to properties', () => {
235
+ const layout = {
236
+ name: 'test-layout',
237
+ displayName: 'Test Layout',
238
+ description: 'A test layout',
239
+ entry: 'template.hbs',
240
+ zones: {},
241
+ options: {
242
+ title: {
243
+ displayName: 'Title',
244
+ description: 'Page title',
245
+ valueType: render_1.LayoutOptionValueType.text,
246
+ },
247
+ showHeader: {
248
+ displayName: 'Show Header',
249
+ description: 'Whether to show header',
250
+ valueType: render_1.LayoutOptionValueType.boolean,
251
+ },
252
+ theme: {
253
+ displayName: 'Theme',
254
+ description: 'Color theme',
255
+ values: ['light', 'dark'],
256
+ },
257
+ },
258
+ };
259
+ const result = (0, normalize_1.normalizeProperties)(layout);
260
+ expect(result).toEqual({
261
+ title: {
262
+ type: 'string',
263
+ title: 'Title',
264
+ description: 'Page title',
265
+ },
266
+ showHeader: {
267
+ type: 'boolean',
268
+ title: 'Show Header',
269
+ description: 'Whether to show header',
270
+ },
271
+ theme: {
272
+ type: 'string',
273
+ title: 'Theme',
274
+ description: 'Color theme',
275
+ enum: ['light', 'dark'],
276
+ },
277
+ });
278
+ });
279
+ it('should return undefined when options is undefined', () => {
280
+ const layout = {
281
+ name: 'test-layout',
282
+ displayName: 'Test Layout',
283
+ description: 'A test layout',
284
+ entry: 'template.hbs',
285
+ zones: {},
286
+ };
287
+ const result = (0, normalize_1.normalizeProperties)(layout);
288
+ expect(result).toBeUndefined();
289
+ });
290
+ it('should return undefined when options is explicitly undefined', () => {
291
+ const layout = {
292
+ name: 'test-layout',
293
+ displayName: 'Test Layout',
294
+ description: 'A test layout',
295
+ entry: 'template.hbs',
296
+ zones: {},
297
+ options: undefined,
298
+ };
299
+ const result = (0, normalize_1.normalizeProperties)(layout);
300
+ expect(result).toBeUndefined();
301
+ });
302
+ it('should handle empty options object', () => {
303
+ const layout = {
304
+ name: 'test-layout',
305
+ displayName: 'Test Layout',
306
+ description: 'A test layout',
307
+ entry: 'template.hbs',
308
+ zones: {},
309
+ options: {},
310
+ };
311
+ const result = (0, normalize_1.normalizeProperties)(layout);
312
+ expect(result).toEqual({});
313
+ });
314
+ });
315
+ });
@@ -13,16 +13,32 @@ export declare function parseZonesList(value: string, previous: Record<string, s
13
13
  [x: string]: string[];
14
14
  };
15
15
  /**
16
- * Parse options list from command line
16
+ * Parse properties list from command line
17
17
  * Supports multiple formats:
18
18
  * - Single pair: 'key=value' or 'key:value'
19
19
  * - Comma-separated list: 'key1=value1,key2=value2' or 'key1:value1,key2:value2'
20
- * - Multiple --options flags: later values override earlier ones
20
+ * - Multiple --properties flags: later values override earlier ones
21
+ *
22
+ * Note: All values are returned as strings. Use normalizeLayoutProperties() to convert
23
+ * boolean properties to actual boolean types based on the layout definition.
21
24
  *
22
25
  * @param value Current value being processed
23
26
  * @param previous Previously processed value (used for multiple flags)
24
- * @returns Record of option names to values
27
+ * @returns Record of property names to string values
25
28
  */
26
- export declare function parseOptionsList(value: string, previous: Record<string, string>): {
29
+ export declare function parsePropertiesList(value: string, previous: Record<string, string>): {
27
30
  [x: string]: string;
28
31
  };
32
+ /**
33
+ * Normalizes layout properties by converting string boolean values to actual booleans
34
+ * based on the layout definition's property types.
35
+ *
36
+ * @param properties The parsed properties from CLI (all strings)
37
+ * @param layoutDefinition The layout definition with property type information
38
+ * @returns Properties with boolean values converted to actual booleans
39
+ */
40
+ export declare function normalizeLayoutProperties(properties: Record<string, string>, layoutDefinition: {
41
+ properties?: Record<string, {
42
+ type?: string;
43
+ }>;
44
+ }): Record<string, string | boolean>;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseOptionsList = exports.parseZonesList = void 0;
3
+ exports.normalizeLayoutProperties = exports.parsePropertiesList = exports.parseZonesList = void 0;
4
+ const render_1 = require("./render");
4
5
  /**
5
6
  * Parse zones list from command line
6
7
  * Supports multiple formats:
@@ -44,41 +45,75 @@ function parseZonesList(value, previous) {
44
45
  }
45
46
  exports.parseZonesList = parseZonesList;
46
47
  /**
47
- * Parse options list from command line
48
+ * Parse properties list from command line
48
49
  * Supports multiple formats:
49
50
  * - Single pair: 'key=value' or 'key:value'
50
51
  * - Comma-separated list: 'key1=value1,key2=value2' or 'key1:value1,key2:value2'
51
- * - Multiple --options flags: later values override earlier ones
52
+ * - Multiple --properties flags: later values override earlier ones
53
+ *
54
+ * Note: All values are returned as strings. Use normalizeLayoutProperties() to convert
55
+ * boolean properties to actual boolean types based on the layout definition.
52
56
  *
53
57
  * @param value Current value being processed
54
58
  * @param previous Previously processed value (used for multiple flags)
55
- * @returns Record of option names to values
59
+ * @returns Record of property names to string values
56
60
  */
57
- function parseOptionsList(value, previous) {
61
+ function parsePropertiesList(value, previous) {
58
62
  const result = Object.assign({}, previous);
59
63
  if (!value)
60
64
  return result;
61
65
  // Split comma-separated values if present
62
66
  const parts = value.includes(',') ? value.split(',') : [value];
63
67
  for (const part of parts) {
64
- let optionName;
65
- let optionValue;
68
+ let propertyName;
69
+ let propertyValue;
66
70
  // Handle both colon and equals separators
67
71
  if (part.includes('=')) {
68
- [optionName, optionValue] = part.split('=');
72
+ [propertyName, propertyValue] = part.split('=');
69
73
  }
70
74
  else if (part.includes(':')) {
71
- [optionName, optionValue] = part.split(':');
75
+ [propertyName, propertyValue] = part.split(':');
72
76
  }
73
77
  else {
74
78
  continue; // Skip invalid format
75
79
  }
76
- // Skip empty option names or values
77
- if (!optionName || !optionValue) {
80
+ // Skip empty property names or values
81
+ if (!propertyName || !propertyValue) {
78
82
  continue;
79
83
  }
80
- result[optionName] = optionValue;
84
+ result[propertyName] = propertyValue;
81
85
  }
82
86
  return result;
83
87
  }
84
- exports.parseOptionsList = parseOptionsList;
88
+ exports.parsePropertiesList = parsePropertiesList;
89
+ /**
90
+ * Normalizes layout properties by converting string boolean values to actual booleans
91
+ * based on the layout definition's property types.
92
+ *
93
+ * @param properties The parsed properties from CLI (all strings)
94
+ * @param layoutDefinition The layout definition with property type information
95
+ * @returns Properties with boolean values converted to actual booleans
96
+ */
97
+ function normalizeLayoutProperties(properties, layoutDefinition) {
98
+ const normalized = Object.assign({}, properties);
99
+ if (!layoutDefinition.properties) {
100
+ return normalized;
101
+ }
102
+ // Convert string "true"/"false" to actual booleans for boolean-type properties
103
+ for (const [key, propertyDef] of Object.entries(layoutDefinition.properties)) {
104
+ if (propertyDef.type === render_1.LayoutPropertyType.boolean && key in normalized) {
105
+ const value = normalized[key];
106
+ if (value === 'true') {
107
+ normalized[key] = true;
108
+ }
109
+ else if (value === 'false') {
110
+ normalized[key] = false;
111
+ }
112
+ else {
113
+ throw new Error(`Invalid boolean value "${value}" for property "${key}". Must be "true" or "false".`);
114
+ }
115
+ }
116
+ }
117
+ return normalized;
118
+ }
119
+ exports.normalizeLayoutProperties = normalizeLayoutProperties;