@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/CHANGELOG.md +33 -0
- package/UPGRADE.md +1 -1
- package/dist/cli.mjs +25 -25
- package/package.json +2 -2
- package/template/preview/shared/api-routes.ts +5 -5
- package/template/preview/shared/preview.ts +61 -4
- package/template/preview/shared/utils.ts +11 -1
- package/template/sections/example-section/settings/design.ts +8 -0
- package/template/sections/example-section/settings/layout.ts +3 -0
- package/template/sections/example-section/settings/translations.ts +13 -0
- package/template/sections/example-section/showcases/1.ts +3 -0
- package/template/sections/example-section/showcases/2.ts +3 -0
- package/template/sections/example-section/showcases/3.ts +3 -0
- package/template/sections/example-section/showcases/translations.ts +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightspeed/crane",
|
|
3
|
-
"version": "3.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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'],
|
|
@@ -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
|
});
|
|
@@ -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
|
},
|