@lightspeed/crane 3.0.0 → 3.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightspeed/crane",
3
- "version": "3.0.0",
3
+ "version": "3.2.0",
4
4
  "type": "module",
5
5
  "bin": "bin/crane.js",
6
6
  "main": "./dist/app.mjs",
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "dependencies": {
67
67
  "@jridgewell/sourcemap-codec": "^1.5.4",
68
- "@lightspeed/crane-api": "2.0.0",
68
+ "@lightspeed/crane-api": "2.2.0",
69
69
  "@lightspeed/eslint-config-crane": "1.1.3",
70
70
  "@types/micromatch": "^4.0.8",
71
71
  "@types/prompts": "^2.4.2",
@@ -435,7 +435,7 @@ async function handleGetBlockConfigFull(
435
435
  */
436
436
  function getAvailableShowcases(blockType: BlockType, blockName: string, distPath: string): string[] {
437
437
  try {
438
- const showcasesPath = path.join(distPath, blockType, blockName, 'js', 'showcases');
438
+ const showcasesPath = path.resolve(distPath, blockType, blockName, 'js', 'showcases');
439
439
 
440
440
  if (!fs.existsSync(showcasesPath)) {
441
441
  console.warn(`[API Routes] Showcases directory not found for ${blockType}/"${blockName}": ${showcasesPath}`);
@@ -511,20 +511,20 @@ async function handleGetTile(
511
511
 
512
512
  const showcaseIds = getShowcaseIds(blockType, blockName, distPath);
513
513
  return showcaseIds.map(async (showcaseId) => {
514
- const { content, design } = await getShowcaseData(
514
+ const { content, design, tileName } = await getShowcaseData(
515
515
  blockType,
516
516
  blockName,
517
517
  showcaseId,
518
518
  distPath,
519
519
  );
520
- return { blockName, showcaseId, content, design };
520
+ return { blockName, showcaseId, content, design, tileName };
521
521
  });
522
522
  });
523
523
 
524
524
  // Wait for all showcase data in parallel
525
525
  const allShowcaseData = await Promise.all(blockPromises);
526
- for (const { blockName, showcaseId, content, design } of allShowcaseData) {
527
- responseData = updateTilesSection(responseData, blockName, showcaseId, content, design);
526
+ for (const { blockName, showcaseId, content, design, tileName } of allShowcaseData) {
527
+ responseData = updateTilesSection(responseData, blockName, showcaseId, content, design, tileName);
528
528
  }
529
529
  res.statusCode = 200;
530
530
  res.setHeader('Content-Type', 'application/json');
@@ -239,25 +239,60 @@ export function mergeDesign(
239
239
  return result;
240
240
  }
241
241
 
242
+ function parseAccordionDesign(comp: any): Record<string, any> {
243
+ const items: Record<string, any> = {};
244
+ Object.entries(comp.items || {}).forEach(([itemName, item]: [string, any]) => {
245
+ const editors: Record<string, any> = {};
246
+ Object.entries(item.editors || {}).forEach(([editorName, editor]: [string, any]) => {
247
+ editors[editorName] = editor.defaults || {};
248
+ });
249
+ items[itemName] = { label: item.label, editors };
250
+ });
251
+ return { sortable: comp.sortable, items };
252
+ }
253
+
254
+ function mergeAccordionShowcase(baseAccordion: Record<string, any>, showcaseAccordion: Record<string, any>): Record<string, any> {
255
+ const mergedItems: Record<string, any> = { ...baseAccordion.items };
256
+ Object.entries(showcaseAccordion.items || {}).forEach(([itemName, showcaseItem]: [string, any]) => {
257
+ const baseItem = mergedItems[itemName];
258
+ if (!baseItem) return;
259
+ mergedItems[itemName] = {
260
+ ...baseItem,
261
+ editors: { ...baseItem.editors, ...(showcaseItem.editors || {}) },
262
+ };
263
+ });
264
+ return { sortable: baseAccordion.sortable, items: mergedItems };
265
+ }
266
+
242
267
  export function designTransformer(design: Record<string, any>, showCaseDesign: Record<string, any>, isApiRender: boolean = false): Record<string, any> {
243
268
  const parsedDesign: Record<string, any> = {};
244
269
  const parsedShowcaseDesign: Record<string, any> = {};
270
+ const accordionKeys: Set<string> = new Set();
271
+
245
272
  Object.entries(design || {}).forEach(([key, comp]) => {
246
273
  // Skip DIVIDER elements - they're UI-only and don't have runtime data
247
- if (comp?.type === 'DIVIDER') {
274
+ if (comp?.type === 'DIVIDER' || comp?.type === 'INFO') {
248
275
  return;
249
276
  }
250
277
  // Skip BACKGROUND - it's handled separately by createBackgroundDesign
251
278
  if (comp?.type === 'BACKGROUND') {
252
279
  return;
253
280
  }
254
- // Use defaults if available, otherwise use empty object
281
+ if (comp?.type === 'ACCORDION') {
282
+ accordionKeys.add(key);
283
+ parsedDesign[key] = parseAccordionDesign(comp);
284
+ return;
285
+ }
255
286
  parsedDesign[key] = comp?.defaults || {};
256
287
  });
257
288
 
258
289
  Object.entries(showCaseDesign || {}).forEach(([key, comp]) => {
259
290
  // Skip DIVIDER elements - they're UI-only and don't have runtime data
260
- if (comp?.type === 'DIVIDER') {
291
+ if (comp?.type === 'DIVIDER' || comp?.type === 'INFO') {
292
+ return;
293
+ }
294
+ if (comp?.type === 'ACCORDION') {
295
+ parsedShowcaseDesign[key] = comp;
261
296
  return;
262
297
  }
263
298
  // Skip BACKGROUND - it's handled separately by createBackgroundDesign
@@ -268,7 +303,24 @@ export function designTransformer(design: Record<string, any>, showCaseDesign: R
268
303
  parsedShowcaseDesign[key] = type == 'TEXT' ? { ...withoutType, visible: true } : withoutType;
269
304
  });
270
305
 
271
- let overridenDesign = mergeDesign(parsedDesign, parsedShowcaseDesign);
306
+ // Merge non-ACCORDION entries via shallow merge, then handle ACCORDION separately
307
+ const nonAccordionDesign: Record<string, any> = {};
308
+ const nonAccordionShowcase: Record<string, any> = {};
309
+ Object.entries(parsedDesign).forEach(([k, v]) => {
310
+ if (!accordionKeys.has(k)) nonAccordionDesign[k] = v;
311
+ });
312
+ Object.entries(parsedShowcaseDesign).forEach(([k, v]) => {
313
+ if (!accordionKeys.has(k)) nonAccordionShowcase[k] = v;
314
+ });
315
+
316
+ let overridenDesign = mergeDesign(nonAccordionDesign, nonAccordionShowcase);
317
+
318
+ accordionKeys.forEach(key => {
319
+ const showcase = parsedShowcaseDesign[key];
320
+ overridenDesign[key] = showcase
321
+ ? mergeAccordionShowcase(parsedDesign[key], showcase)
322
+ : parsedDesign[key];
323
+ });
272
324
 
273
325
  try {
274
326
  overridenDesign = updateHexColors(overridenDesign);
@@ -564,9 +616,14 @@ export async function getShowcaseData(blockType: BlockType, blockName: string, s
564
616
  true,
565
617
  );
566
618
 
619
+ // Resolve tileName from showcase blockName translation
620
+ const blockNameKey = (showcase as any).default?.blockName;
621
+ const tileName = blockNameKey ? (showcaseTranslations as any).default.en[blockNameKey] : undefined;
622
+
567
623
  return {
568
624
  content: overriddenContent,
569
625
  design: overriddenDesign,
626
+ tileName,
570
627
  };
571
628
  }
572
629
 
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
+ import * as urlFromNode from 'url';
4
5
 
5
6
  import { getShowcaseState, BlockType, BLOCK_TYPES } from './preview';
6
7
 
@@ -50,7 +51,11 @@ function addCacheBust(url: string): string {
50
51
  * Note: server.js modules are NOT loaded here - they are rendered via renderServerModule
51
52
  */
52
53
  export async function loadModule(modulePath: string): Promise<unknown> {
53
- return import(/* @vite-ignore */ addCacheBust(modulePath));
54
+ const isWindows = typeof process !== 'undefined' && process.platform === 'win32';
55
+ const importPath = isWindows && path.isAbsolute(modulePath)
56
+ ? urlFromNode.pathToFileURL(modulePath).href
57
+ : modulePath;
58
+ return import(/* @vite-ignore */ addCacheBust(importPath));
54
59
  }
55
60
 
56
61
  /**
@@ -96,6 +101,7 @@ export async function fetchTiles(authToken?: string, tilesUrl?: string): Promise
96
101
  * @param showcaseId - The showcase ID to match
97
102
  * @param content - The new content to replace
98
103
  * @param design - The new design to replace
104
+ * @param tileName - The new tile name to replace
99
105
  * @returns The updated tiles response object
100
106
  */
101
107
  export function updateTilesSection(
@@ -104,6 +110,7 @@ export function updateTilesSection(
104
110
  showcaseId: string,
105
111
  content: any,
106
112
  design: any,
113
+ tileName?: string,
107
114
  ): any {
108
115
  const targetPattern = `_${sectionName}_${showcaseId}`;
109
116
 
@@ -115,6 +122,9 @@ export function updateTilesSection(
115
122
  const layout = design.layout || 'CUSTOM_LAYOUT';
116
123
  item.design = design;
117
124
  item.design.layout = layout;
125
+ if (tileName) {
126
+ item.tileName = tileName;
127
+ }
118
128
  }
119
129
  return item;
120
130
  });
@@ -51,6 +51,14 @@ export default {
51
51
  color: '#333',
52
52
  },
53
53
  }),
54
+ info: design.info({
55
+ label: '$label.info.design.label',
56
+ description: '$label.info.design.description',
57
+ button: {
58
+ label: '$label.info.design.button.label',
59
+ link: 'https://example.com',
60
+ },
61
+ }),
54
62
  background: design.background({
55
63
  label: '$label.background.label',
56
64
  colors: ['#FFFFFF66', '#0000004D', '#00000099', '#64C7FF66', '#F9947266', '#C794CD66', '#FFD17466'],
@@ -30,6 +30,9 @@ export default [
30
30
  layout.designOverride.background({
31
31
  fieldName: 'background',
32
32
  }),
33
+ layout.designOverride.info({
34
+ fieldName: 'info',
35
+ }),
33
36
  ],
34
37
  }),
35
38
  ];
@@ -45,6 +45,10 @@ export default translation.init({
45
45
  '$label.info.label': 'Content.ts: Info',
46
46
  '$label.info.description': 'Content.ts: This is info section where you can provide some useful tips regarding custom section usage',
47
47
  '$label.info.button.label': 'Content.ts: Learn more',
48
+
49
+ '$label.info.design.label': 'Design.ts: Info',
50
+ '$label.info.design.description': 'Design.ts: This is info section where you can provide some useful tips regarding custom section usage',
51
+ '$label.info.design.button.label': 'Design.ts: Learn more',
48
52
  },
49
53
 
50
54
  nl: {
@@ -87,6 +91,10 @@ export default translation.init({
87
91
  '$label.info.label': 'Informatie',
88
92
  '$label.info.description': 'Dit is een informatiedeel waar je enkele nuttige tips kunt geven over het gebruik van aangepaste secties',
89
93
  '$label.info.button.label': 'Meer informatie',
94
+
95
+ '$label.info.design.label': 'Design.ts: Informatie',
96
+ '$label.info.design.description': 'Design.ts: Dit is een informatiedeel waar je enkele nuttige tips kunt geven over het gebruik van aangepaste secties',
97
+ '$label.info.design.button.label': 'Design.ts: Meer informatie',
90
98
  },
91
99
 
92
100
  fr: {
@@ -130,5 +138,10 @@ export default translation.init({
130
138
  '$label.info.description': 'Content.ts: Ceci est une section d\'information où vous '
131
139
  + 'pouvez fournir des conseils utiles sur l\'utilisation de la section personnalisée',
132
140
  '$label.info.button.label': 'En savoir plus',
141
+
142
+ '$label.info.design.label': 'Design.ts: Info',
143
+ '$label.info.design.description': 'Design.ts: Ceci est une section d\'information où vous '
144
+ + 'pouvez fournir des conseils utiles sur l\'utilisation de la section personnalisée',
145
+ '$label.info.design.button.label': 'Design.ts: En savoir plus',
133
146
  },
134
147
  });
@@ -179,5 +179,8 @@ export default showcase.init({
179
179
  overlay: 'GRADIENT',
180
180
  color: ['#FFFFFF', '#000000'],
181
181
  }),
182
+ info: design.default.info({
183
+ description: '$label.showcase_1.info.design.text',
184
+ }),
182
185
  },
183
186
  });
@@ -170,5 +170,8 @@ export default showcase.init({
170
170
  overlay: 'GRADIENT',
171
171
  color: ['#FFFFFF', '#000000'],
172
172
  }),
173
+ info: design.default.info({
174
+ description: '$label.showcase_2.info.design.text',
175
+ }),
173
176
  },
174
177
  });
@@ -178,5 +178,8 @@ export default showcase.init({
178
178
  overlay: 'GRADIENT',
179
179
  color: ['#000000', '#00000033'],
180
180
  }),
181
+ info: design.default.info({
182
+ description: '$label.showcase_3.info.design.text',
183
+ }),
181
184
  },
182
185
  });
@@ -34,6 +34,9 @@ export default translation.init({
34
34
  '$label.showcase_3.info.text': 'Showcase 3: This is info section where you can provide some useful tips regarding custom section usage',
35
35
  '$label.showcase_1.info.button.title': 'Showcase 1: Learn',
36
36
  '$label.showcase_3.info.button.title': 'Showcase 3: More',
37
+ '$label.showcase_1.info.design.text': 'Design.ts: Showcase 1 info description for design settings',
38
+ '$label.showcase_2.info.design.text': 'Design.ts: Showcase 2 info description for design settings',
39
+ '$label.showcase_3.info.design.text': 'Design.ts: Showcase 3 info description for design settings',
37
40
  '$label.showcase_1.button.defaults.title': 'Button Title',
38
41
  '$label.showcase_2.button.defaults.title': 'Button Title',
39
42
  },
@@ -70,6 +73,9 @@ export default translation.init({
70
73
  '$label.showcase_2.info.text': 'Hier is een gebied voor nuttige tips over het gebruik van aangepaste secties',
71
74
  '$label.showcase_1.info.button.title': 'Leren',
72
75
  '$label.showcase_3.info.button.title': 'Meer',
76
+ '$label.showcase_1.info.design.text': 'Showcase 1 infobeschrijving voor ontwerpinstellingen',
77
+ '$label.showcase_2.info.design.text': 'Showcase 2 infobeschrijving voor ontwerpinstellingen',
78
+ '$label.showcase_3.info.design.text': 'Showcase 3 infobeschrijving voor ontwerpinstellingen',
73
79
  '$label.showcase_1.button.defaults.title': 'Knoptitel',
74
80
  '$label.showcase_2.button.defaults.title': 'Knoptitel',
75
81
  },
@@ -109,6 +115,9 @@ export default translation.init({
109
115
  + 'l\'utilisation de la section personnalisée',
110
116
  '$label.showcase_1.info.button.title': 'Showcase 1: Apprendre',
111
117
  '$label.showcase_3.info.button.title': 'Showcase 3: Plus',
118
+ '$label.showcase_1.info.design.text': 'Description info du showcase 1 pour les paramètres de design',
119
+ '$label.showcase_2.info.design.text': 'Description info du showcase 2 pour les paramètres de design',
120
+ '$label.showcase_3.info.design.text': 'Description info du showcase 3 pour les paramètres de design',
112
121
  '$label.showcase_1.button.defaults.title': 'Titre du bouton',
113
122
  '$label.showcase_2.button.defaults.title': 'Titre du bouton',
114
123
  },