@ikas/component-cli 1.4.0-beta.5 → 1.4.0-beta.50
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/dist/commands/add-sections-to-page.d.ts +3 -0
- package/dist/commands/add-sections-to-page.d.ts.map +1 -0
- package/dist/commands/add-sections-to-page.js +39 -0
- package/dist/commands/add-sections-to-page.js.map +1 -0
- package/dist/commands/add-to-page.d.ts +3 -0
- package/dist/commands/add-to-page.d.ts.map +1 -0
- package/dist/commands/add-to-page.js +41 -0
- package/dist/commands/add-to-page.js.map +1 -0
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +5 -165
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +514 -114
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/create-design-tokens.d.ts +7 -0
- package/dist/commands/create-design-tokens.d.ts.map +1 -0
- package/dist/commands/create-design-tokens.js +127 -0
- package/dist/commands/create-design-tokens.js.map +1 -0
- package/dist/commands/create-global-variable.d.ts +3 -0
- package/dist/commands/create-global-variable.d.ts.map +1 -0
- package/dist/commands/create-global-variable.js +53 -0
- package/dist/commands/create-global-variable.js.map +1 -0
- package/dist/commands/create-page.d.ts +3 -0
- package/dist/commands/create-page.d.ts.map +1 -0
- package/dist/commands/create-page.js +31 -0
- package/dist/commands/create-page.js.map +1 -0
- package/dist/commands/delete-theme-globals.d.ts +4 -0
- package/dist/commands/delete-theme-globals.d.ts.map +1 -0
- package/dist/commands/delete-theme-globals.js +48 -0
- package/dist/commands/delete-theme-globals.js.map +1 -0
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +297 -25
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/get-component-props.d.ts +3 -0
- package/dist/commands/get-component-props.d.ts.map +1 -0
- package/dist/commands/get-component-props.js +32 -0
- package/dist/commands/get-component-props.js.map +1 -0
- package/dist/commands/get-page-by-type.d.ts +3 -0
- package/dist/commands/get-page-by-type.d.ts.map +1 -0
- package/dist/commands/get-page-by-type.js +25 -0
- package/dist/commands/get-page-by-type.js.map +1 -0
- package/dist/commands/get-section-values.d.ts +3 -0
- package/dist/commands/get-section-values.d.ts.map +1 -0
- package/dist/commands/get-section-values.js +39 -0
- package/dist/commands/get-section-values.js.map +1 -0
- package/dist/commands/import.d.ts +3 -0
- package/dist/commands/import.d.ts.map +1 -0
- package/dist/commands/import.js +25 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/list-entities.d.ts +3 -0
- package/dist/commands/list-entities.d.ts.map +1 -0
- package/dist/commands/list-entities.js +32 -0
- package/dist/commands/list-entities.js.map +1 -0
- package/dist/commands/list-imported.d.ts +3 -0
- package/dist/commands/list-imported.d.ts.map +1 -0
- package/dist/commands/list-imported.js +25 -0
- package/dist/commands/list-imported.js.map +1 -0
- package/dist/commands/list-page-sections.d.ts +3 -0
- package/dist/commands/list-page-sections.d.ts.map +1 -0
- package/dist/commands/list-page-sections.js +25 -0
- package/dist/commands/list-page-sections.js.map +1 -0
- package/dist/commands/list-pages.d.ts +3 -0
- package/dist/commands/list-pages.d.ts.map +1 -0
- package/dist/commands/list-pages.js +21 -0
- package/dist/commands/list-pages.js.map +1 -0
- package/dist/commands/list-theme-globals.d.ts +3 -0
- package/dist/commands/list-theme-globals.d.ts.map +1 -0
- package/dist/commands/list-theme-globals.js +22 -0
- package/dist/commands/list-theme-globals.js.map +1 -0
- package/dist/commands/publish-theme.d.ts +3 -0
- package/dist/commands/publish-theme.d.ts.map +1 -0
- package/dist/commands/publish-theme.js +29 -0
- package/dist/commands/publish-theme.js.map +1 -0
- package/dist/commands/search-products.d.ts +3 -0
- package/dist/commands/search-products.d.ts.map +1 -0
- package/dist/commands/search-products.js +40 -0
- package/dist/commands/search-products.js.map +1 -0
- package/dist/commands/update-global-variable.d.ts +3 -0
- package/dist/commands/update-global-variable.d.ts.map +1 -0
- package/dist/commands/update-global-variable.js +47 -0
- package/dist/commands/update-global-variable.js.map +1 -0
- package/dist/commands/update-page-sections.d.ts +3 -0
- package/dist/commands/update-page-sections.d.ts.map +1 -0
- package/dist/commands/update-page-sections.js +39 -0
- package/dist/commands/update-page-sections.js.map +1 -0
- package/dist/commands/update-section-prop.d.ts +3 -0
- package/dist/commands/update-section-prop.d.ts.map +1 -0
- package/dist/commands/update-section-prop.js +59 -0
- package/dist/commands/update-section-prop.js.map +1 -0
- package/dist/commands/upload-image.d.ts +3 -0
- package/dist/commands/upload-image.d.ts.map +1 -0
- package/dist/commands/upload-image.js +38 -0
- package/dist/commands/upload-image.js.map +1 -0
- package/dist/commands/upload-images.d.ts +3 -0
- package/dist/commands/upload-images.d.ts.map +1 -0
- package/dist/commands/upload-images.js +48 -0
- package/dist/commands/upload-images.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +28 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/compile.d.ts +4 -1
- package/dist/utils/compile.d.ts.map +1 -1
- package/dist/utils/compile.js +517 -48
- package/dist/utils/compile.js.map +1 -1
- package/dist/utils/component-helpers.d.ts +29 -2
- package/dist/utils/component-helpers.d.ts.map +1 -1
- package/dist/utils/component-helpers.js +102 -15
- package/dist/utils/component-helpers.js.map +1 -1
- package/dist/utils/editor-action-client.d.ts +28 -0
- package/dist/utils/editor-action-client.d.ts.map +1 -0
- package/dist/utils/editor-action-client.js +116 -0
- package/dist/utils/editor-action-client.js.map +1 -0
- package/dist/utils/load-image.d.ts +16 -0
- package/dist/utils/load-image.d.ts.map +1 -0
- package/dist/utils/load-image.js +50 -0
- package/dist/utils/load-image.js.map +1 -0
- package/dist/utils/websocket-server.d.ts +128 -1
- package/dist/utils/websocket-server.d.ts.map +1 -1
- package/dist/utils/websocket-server.js +116 -0
- package/dist/utils/websocket-server.js.map +1 -1
- package/package.json +1 -1
- package/dist/commands/create.d.ts +0 -9
- package/dist/commands/create.d.ts.map +0 -1
- package/dist/commands/create.js +0 -9
- package/dist/commands/create.js.map +0 -1
- package/dist/commands/proxy.d.ts +0 -39
- package/dist/commands/proxy.d.ts.map +0 -1
- package/dist/commands/proxy.js +0 -212
- package/dist/commands/proxy.js.map +0 -1
package/dist/commands/config.js
CHANGED
|
@@ -2,6 +2,16 @@ import { Command } from "commander";
|
|
|
2
2
|
import * as fs from "fs";
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import { PROP_TYPES, toPascalCase, generateTypesFile, generateGlobalTypesFile, collectUsedEnumIds, generateComponentFile, generateStylesFile, generateProjectId, generateComponentId, generateUniqueId, updateBarrelExport, findPropGroup, collectPropGroupIds, movePropGroupInTree, validateFilteredComponentIds, } from "../utils/component-helpers.js";
|
|
5
|
+
import { runEditorAction, printResultAndExit, printErrorAndExit } from "../utils/editor-action-client.js";
|
|
6
|
+
async function listLocales(port) {
|
|
7
|
+
try {
|
|
8
|
+
const result = await runEditorAction("list-locales", {}, port ? { port } : {});
|
|
9
|
+
printResultAndExit(result);
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
printErrorAndExit(e);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
5
15
|
function loadConfig() {
|
|
6
16
|
const configPath = path.resolve(process.cwd(), "ikas.config.json");
|
|
7
17
|
if (!fs.existsSync(configPath)) {
|
|
@@ -17,9 +27,48 @@ function loadConfig() {
|
|
|
17
27
|
function saveConfig(configPath, config) {
|
|
18
28
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
19
29
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a component by canonical id (preferred) or exact PascalCase name.
|
|
32
|
+
* No silent normalization: pass --id from `config list-components`, or pass --name
|
|
33
|
+
* exactly as stored in ikas.config.json. Exits with a structured JSON error
|
|
34
|
+
* (listing valid {id, name} pairs) if zero/both/missing.
|
|
35
|
+
*/
|
|
36
|
+
function resolveComponent(config, ref) {
|
|
37
|
+
const id = ref.id?.trim() || undefined;
|
|
38
|
+
const name = ref.name?.trim() || undefined;
|
|
39
|
+
if (!id && !name) {
|
|
40
|
+
console.log(JSON.stringify({
|
|
41
|
+
success: false,
|
|
42
|
+
error: "Component reference required: pass --id (preferred) or --name (exact PascalCase). " +
|
|
43
|
+
"Run `ikas-component config list-components` to see canonical ids.",
|
|
44
|
+
validComponents: config.components.map((c) => ({ id: c.id, name: c.name })),
|
|
45
|
+
}));
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
if (id && name) {
|
|
49
|
+
console.log(JSON.stringify({
|
|
50
|
+
success: false,
|
|
51
|
+
error: "Specify only one of --id or --name, not both.",
|
|
52
|
+
}));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const found = id
|
|
56
|
+
? config.components.find((c) => c.id === id)
|
|
57
|
+
: config.components.find((c) => c.name === name);
|
|
58
|
+
if (!found) {
|
|
59
|
+
console.log(JSON.stringify({
|
|
60
|
+
success: false,
|
|
61
|
+
error: id
|
|
62
|
+
? `Component not found: id="${id}".`
|
|
63
|
+
: `Component not found: name="${name}". Names are matched exactly (PascalCase, no normalization).`,
|
|
64
|
+
lookup: id ? { id } : { name },
|
|
65
|
+
validComponents: config.components.map((c) => ({ id: c.id, name: c.name })),
|
|
66
|
+
hint: "Use --id (preferred) or exact PascalCase --name. " +
|
|
67
|
+
"Run `ikas-component config list-components` to see canonical ids.",
|
|
68
|
+
}));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
return found;
|
|
23
72
|
}
|
|
24
73
|
function getComponentNames(config) {
|
|
25
74
|
return config.components.map((c) => c.name);
|
|
@@ -105,6 +154,19 @@ function camelCaseToDisplayName(name) {
|
|
|
105
154
|
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
106
155
|
.replace(/^./, (c) => c.toUpperCase());
|
|
107
156
|
}
|
|
157
|
+
const ALLOWED_PROP_FIELDS = new Set([
|
|
158
|
+
"name",
|
|
159
|
+
"displayName",
|
|
160
|
+
"type",
|
|
161
|
+
"required",
|
|
162
|
+
"description",
|
|
163
|
+
"defaultValue",
|
|
164
|
+
"groupId",
|
|
165
|
+
"typeId",
|
|
166
|
+
"enumTypeId",
|
|
167
|
+
"filteredComponentIds",
|
|
168
|
+
"privateVarMap",
|
|
169
|
+
]);
|
|
108
170
|
/**
|
|
109
171
|
* Parse and validate the --props JSON flag for add-component.
|
|
110
172
|
* Returns validated ComponentProp[] or exits with error.
|
|
@@ -132,6 +194,16 @@ async function parsePropsFlag(propsJson, config, configPath) {
|
|
|
132
194
|
const props = [];
|
|
133
195
|
for (const raw of rawProps) {
|
|
134
196
|
const r = raw;
|
|
197
|
+
const unknownFields = Object.keys(r).filter((k) => !ALLOWED_PROP_FIELDS.has(k));
|
|
198
|
+
if (unknownFields.length > 0) {
|
|
199
|
+
console.log(JSON.stringify({
|
|
200
|
+
success: false,
|
|
201
|
+
error: `Unknown field(s) in --props entry: ${unknownFields.join(", ")}. ` +
|
|
202
|
+
`Allowed: ${[...ALLOWED_PROP_FIELDS].join(", ")}. ` +
|
|
203
|
+
`Note: use "groupId" (not "group") and "defaultValue" (not "default") in --props JSON.`,
|
|
204
|
+
}));
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
135
207
|
if (!r.name || typeof r.name !== "string") {
|
|
136
208
|
console.log(JSON.stringify({
|
|
137
209
|
success: false,
|
|
@@ -172,6 +244,14 @@ async function parsePropsFlag(propsJson, config, configPath) {
|
|
|
172
244
|
}));
|
|
173
245
|
process.exit(1);
|
|
174
246
|
}
|
|
247
|
+
// Validate LINK / LIST_OF_LINK defaultValue shape (reject JSON strings and legacy { href })
|
|
248
|
+
if ((propType === "LINK" || propType === "LIST_OF_LINK") && r.defaultValue !== undefined) {
|
|
249
|
+
const linkError = validateLinkDefaultValue(propType, r.defaultValue, r.name);
|
|
250
|
+
if (linkError) {
|
|
251
|
+
console.log(JSON.stringify({ success: false, error: linkError }));
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
175
255
|
// Auto-generate displayName if omitted
|
|
176
256
|
const displayName = typeof r.displayName === "string" && r.displayName
|
|
177
257
|
? r.displayName
|
|
@@ -293,6 +373,7 @@ async function addComponent(name, options) {
|
|
|
293
373
|
const newComponent = {
|
|
294
374
|
id: componentId,
|
|
295
375
|
name: pascalName,
|
|
376
|
+
...(options.displayName ? { displayName: options.displayName } : {}),
|
|
296
377
|
entry: `./src/components/${pascalName}/index.tsx`,
|
|
297
378
|
styles: `./src/components/${pascalName}/styles.css`,
|
|
298
379
|
...(componentType === "section" ? { type: "section" } : {}),
|
|
@@ -322,16 +403,9 @@ async function addComponent(name, options) {
|
|
|
322
403
|
],
|
|
323
404
|
}));
|
|
324
405
|
}
|
|
325
|
-
async function addProp(
|
|
406
|
+
async function addProp(ref, options) {
|
|
326
407
|
const { config, configPath } = loadConfig();
|
|
327
|
-
const component =
|
|
328
|
-
if (!component) {
|
|
329
|
-
console.log(JSON.stringify({
|
|
330
|
-
success: false,
|
|
331
|
-
error: `Component "${componentName}" not found. Available: ${getComponentNames(config).join(", ")}`,
|
|
332
|
-
}));
|
|
333
|
-
process.exit(1);
|
|
334
|
-
}
|
|
408
|
+
const component = resolveComponent(config, ref);
|
|
335
409
|
// Validate prop type
|
|
336
410
|
const propType = options.type.toUpperCase();
|
|
337
411
|
if (!PROP_TYPES.includes(propType)) {
|
|
@@ -434,14 +508,22 @@ async function addProp(componentName, options) {
|
|
|
434
508
|
process.exit(1);
|
|
435
509
|
}
|
|
436
510
|
}
|
|
511
|
+
const parsedDefaultValue = options.defaultValue !== undefined ? parseDefaultValue(options.defaultValue, propType) : undefined;
|
|
512
|
+
if ((propType === "LINK" || propType === "LIST_OF_LINK") && parsedDefaultValue !== undefined) {
|
|
513
|
+
const linkError = validateLinkDefaultValue(propType, parsedDefaultValue, options.name);
|
|
514
|
+
if (linkError) {
|
|
515
|
+
console.log(JSON.stringify({ success: false, error: linkError }));
|
|
516
|
+
process.exit(1);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
437
519
|
const newProp = {
|
|
438
520
|
name: options.name,
|
|
439
521
|
displayName: options.displayName,
|
|
440
522
|
type: propType,
|
|
441
523
|
required: options.required ?? false,
|
|
442
524
|
...(options.description ? { description: options.description } : {}),
|
|
443
|
-
...(
|
|
444
|
-
? { defaultValue:
|
|
525
|
+
...(parsedDefaultValue !== undefined
|
|
526
|
+
? { defaultValue: parsedDefaultValue }
|
|
445
527
|
: {}),
|
|
446
528
|
...(options.group ? { groupId: options.group } : {}),
|
|
447
529
|
...(options.typeId ? { typeId: options.typeId } : {}),
|
|
@@ -475,20 +557,73 @@ function parseDefaultValue(value, propType) {
|
|
|
475
557
|
return Number(value);
|
|
476
558
|
case "BOOLEAN":
|
|
477
559
|
return value === "true";
|
|
560
|
+
case "LINK":
|
|
561
|
+
case "LIST_OF_LINK":
|
|
562
|
+
// The --default-value flag arrives as a string; LINK values must be stored as a
|
|
563
|
+
// typed object. Parse it here; if it is not valid JSON, return the raw string so
|
|
564
|
+
// validateLinkDefaultValue can report a precise error.
|
|
565
|
+
try {
|
|
566
|
+
return JSON.parse(value);
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
return value;
|
|
570
|
+
}
|
|
478
571
|
default:
|
|
479
572
|
return value;
|
|
480
573
|
}
|
|
481
574
|
}
|
|
482
|
-
|
|
575
|
+
const VALID_LINK_TYPES = ["PAGE", "EXTERNAL", "FILE"];
|
|
576
|
+
const LINK_SHAPE_HELP = 'A link must be an object: { "linkType": "EXTERNAL", "label": "Text", "externalLink": "https://...", "subLinks": [] } ' +
|
|
577
|
+
'for external links, or { "linkType": "PAGE", "label": "Text", "pageType": "INDEX", "subLinks": [] } for store pages. ' +
|
|
578
|
+
"Do NOT pass a JSON string or a { label, href } shape.";
|
|
579
|
+
// Returns the index of the first array element that fails `fn`, as an error string, or null.
|
|
580
|
+
const firstError = (arr, fn) => arr.reduce((acc, item, index) => acc ?? fn(item, index), null);
|
|
581
|
+
const validateSingleLink = (link, propName, path) => {
|
|
582
|
+
const where = `defaultValue for prop "${propName}"${path}`;
|
|
583
|
+
if (typeof link === "string") {
|
|
584
|
+
return `${where} is a JSON string but must be an object. ${LINK_SHAPE_HELP}`;
|
|
585
|
+
}
|
|
586
|
+
if (!link || typeof link !== "object" || Array.isArray(link)) {
|
|
587
|
+
return `${where} must be a link object. ${LINK_SHAPE_HELP}`;
|
|
588
|
+
}
|
|
589
|
+
const l = link;
|
|
590
|
+
if ("href" in l && !("linkType" in l)) {
|
|
591
|
+
return `${where} uses a legacy { href } shape without "linkType". Use "linkType" + "externalLink"/"pageType" instead. ${LINK_SHAPE_HELP}`;
|
|
592
|
+
}
|
|
593
|
+
if (typeof l.linkType !== "string" || !VALID_LINK_TYPES.includes(l.linkType)) {
|
|
594
|
+
return `${where} must have a valid "linkType" (one of ${VALID_LINK_TYPES.join(", ")}). ${LINK_SHAPE_HELP}`;
|
|
595
|
+
}
|
|
596
|
+
if (l.subLinks !== undefined && !Array.isArray(l.subLinks)) {
|
|
597
|
+
return `${where}: "subLinks" must be an array (use [] when there are none).`;
|
|
598
|
+
}
|
|
599
|
+
if (Array.isArray(l.subLinks)) {
|
|
600
|
+
return firstError(l.subLinks, (sl, i) => validateSingleLink(sl, propName, `${path}.subLinks[${i}]`));
|
|
601
|
+
}
|
|
602
|
+
return null;
|
|
603
|
+
};
|
|
604
|
+
// Validates a LINK / LIST_OF_LINK defaultValue. Returns an error message, or null if valid.
|
|
605
|
+
// These defaults are a frequent source of bad data: agents tend to pass a JSON string or a
|
|
606
|
+
// legacy { label, href } shape. Rejecting them at authoring time keeps malformed link values
|
|
607
|
+
// out of published themes.
|
|
608
|
+
function validateLinkDefaultValue(propType, value, propName) {
|
|
609
|
+
if (value === undefined || value === null)
|
|
610
|
+
return null;
|
|
611
|
+
if (propType === "LINK") {
|
|
612
|
+
return validateSingleLink(value, propName, "");
|
|
613
|
+
}
|
|
614
|
+
// LIST_OF_LINK
|
|
615
|
+
if (typeof value === "string") {
|
|
616
|
+
return `defaultValue for prop "${propName}" is a JSON string but must be an object { "links": [...] }. ${LINK_SHAPE_HELP}`;
|
|
617
|
+
}
|
|
618
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || !Array.isArray(value.links)) {
|
|
619
|
+
return `defaultValue for prop "${propName}" must be an object with a "links" array: { "links": [ <link>, ... ] }. ${LINK_SHAPE_HELP}`;
|
|
620
|
+
}
|
|
621
|
+
const links = value.links;
|
|
622
|
+
return firstError(links, (link, i) => validateSingleLink(link, propName, `.links[${i}]`));
|
|
623
|
+
}
|
|
624
|
+
function updateProp(ref, options) {
|
|
483
625
|
const { config, configPath } = loadConfig();
|
|
484
|
-
const component =
|
|
485
|
-
if (!component) {
|
|
486
|
-
console.log(JSON.stringify({
|
|
487
|
-
success: false,
|
|
488
|
-
error: `Component "${componentName}" not found. Available: ${getComponentNames(config).join(", ")}`,
|
|
489
|
-
}));
|
|
490
|
-
process.exit(1);
|
|
491
|
-
}
|
|
626
|
+
const component = resolveComponent(config, ref);
|
|
492
627
|
const propIndex = component.props.findIndex((p) => p.name === options.prop);
|
|
493
628
|
if (propIndex === -1) {
|
|
494
629
|
console.log(JSON.stringify({
|
|
@@ -519,7 +654,15 @@ function updateProp(componentName, options) {
|
|
|
519
654
|
prop.description = options.description || undefined;
|
|
520
655
|
}
|
|
521
656
|
if (options.defaultValue !== undefined) {
|
|
522
|
-
|
|
657
|
+
const parsed = parseDefaultValue(options.defaultValue, prop.type);
|
|
658
|
+
if (prop.type === "LINK" || prop.type === "LIST_OF_LINK") {
|
|
659
|
+
const linkError = validateLinkDefaultValue(prop.type, parsed, prop.name);
|
|
660
|
+
if (linkError) {
|
|
661
|
+
console.log(JSON.stringify({ success: false, error: linkError }));
|
|
662
|
+
process.exit(1);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
prop.defaultValue = parsed;
|
|
523
666
|
}
|
|
524
667
|
if (options.group !== undefined) {
|
|
525
668
|
if (options.group === "" || options.group === "none") {
|
|
@@ -622,16 +765,9 @@ function updateProp(componentName, options) {
|
|
|
622
765
|
},
|
|
623
766
|
}));
|
|
624
767
|
}
|
|
625
|
-
function removeProp(
|
|
768
|
+
function removeProp(ref, options) {
|
|
626
769
|
const { config, configPath } = loadConfig();
|
|
627
|
-
const component =
|
|
628
|
-
if (!component) {
|
|
629
|
-
console.log(JSON.stringify({
|
|
630
|
-
success: false,
|
|
631
|
-
error: `Component "${componentName}" not found. Available: ${getComponentNames(config).join(", ")}`,
|
|
632
|
-
}));
|
|
633
|
-
process.exit(1);
|
|
634
|
-
}
|
|
770
|
+
const component = resolveComponent(config, ref);
|
|
635
771
|
const propIndex = component.props.findIndex((p) => p.name === options.prop);
|
|
636
772
|
if (propIndex === -1) {
|
|
637
773
|
console.log(JSON.stringify({
|
|
@@ -652,21 +788,30 @@ function removeProp(componentName, options) {
|
|
|
652
788
|
remainingProps: component.props.map((p) => p.name),
|
|
653
789
|
}));
|
|
654
790
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
791
|
+
/**
|
|
792
|
+
* Strip a deleted component's ID from every other component's COMPONENT/COMPONENT_LIST
|
|
793
|
+
* `filteredComponentIds` allowlist. Empty arrays are deleted to keep the field optional
|
|
794
|
+
* (mirrors the editor-side convention).
|
|
795
|
+
*/
|
|
796
|
+
function pruneFilteredComponentIdRefs(config, removedId) {
|
|
797
|
+
for (const component of config.components) {
|
|
798
|
+
for (const prop of component.props || []) {
|
|
799
|
+
if (!prop.filteredComponentIds)
|
|
800
|
+
continue;
|
|
801
|
+
prop.filteredComponentIds = prop.filteredComponentIds.filter((id) => id !== removedId);
|
|
802
|
+
if (prop.filteredComponentIds.length === 0)
|
|
803
|
+
delete prop.filteredComponentIds;
|
|
804
|
+
}
|
|
665
805
|
}
|
|
666
|
-
|
|
806
|
+
}
|
|
807
|
+
function removeComponent(ref) {
|
|
808
|
+
const { config, configPath } = loadConfig();
|
|
809
|
+
const component = resolveComponent(config, ref);
|
|
810
|
+
const componentIndex = config.components.indexOf(component);
|
|
667
811
|
const componentDir = path.resolve(process.cwd(), path.dirname(component.entry));
|
|
668
|
-
// Remove component from config
|
|
812
|
+
// Remove component from config and strip orphaned references from remaining components
|
|
669
813
|
config.components.splice(componentIndex, 1);
|
|
814
|
+
pruneFilteredComponentIdRefs(config, component.id);
|
|
670
815
|
saveConfig(configPath, config);
|
|
671
816
|
// Remove component directory
|
|
672
817
|
if (fs.existsSync(componentDir)) {
|
|
@@ -676,21 +821,15 @@ function removeComponent(options) {
|
|
|
676
821
|
updateBarrelExport(process.cwd(), getComponentNames(config));
|
|
677
822
|
console.log(JSON.stringify({
|
|
678
823
|
success: true,
|
|
679
|
-
|
|
824
|
+
removedComponentId: component.id,
|
|
825
|
+
removedComponentName: component.name,
|
|
680
826
|
removedDirectory: path.relative(process.cwd(), componentDir),
|
|
681
|
-
remainingComponents:
|
|
827
|
+
remainingComponents: config.components.map((c) => ({ id: c.id, name: c.name })),
|
|
682
828
|
}));
|
|
683
829
|
}
|
|
684
|
-
function addPropGroup(
|
|
830
|
+
function addPropGroup(ref, options) {
|
|
685
831
|
const { config, configPath } = loadConfig();
|
|
686
|
-
const component =
|
|
687
|
-
if (!component) {
|
|
688
|
-
console.log(JSON.stringify({
|
|
689
|
-
success: false,
|
|
690
|
-
error: `Component "${componentName}" not found. Available: ${getComponentNames(config).join(", ")}`,
|
|
691
|
-
}));
|
|
692
|
-
process.exit(1);
|
|
693
|
-
}
|
|
832
|
+
const component = resolveComponent(config, ref);
|
|
694
833
|
if (!component.propGroups)
|
|
695
834
|
component.propGroups = [];
|
|
696
835
|
// Check uniqueness
|
|
@@ -741,16 +880,9 @@ function addPropGroup(componentName, options) {
|
|
|
741
880
|
},
|
|
742
881
|
}));
|
|
743
882
|
}
|
|
744
|
-
function updatePropGroup(
|
|
883
|
+
function updatePropGroup(ref, options) {
|
|
745
884
|
const { config, configPath } = loadConfig();
|
|
746
|
-
const component =
|
|
747
|
-
if (!component) {
|
|
748
|
-
console.log(JSON.stringify({
|
|
749
|
-
success: false,
|
|
750
|
-
error: `Component "${componentName}" not found. Available: ${getComponentNames(config).join(", ")}`,
|
|
751
|
-
}));
|
|
752
|
-
process.exit(1);
|
|
753
|
-
}
|
|
885
|
+
const component = resolveComponent(config, ref);
|
|
754
886
|
if (!component.propGroups || component.propGroups.length === 0) {
|
|
755
887
|
console.log(JSON.stringify({
|
|
756
888
|
success: false,
|
|
@@ -780,16 +912,9 @@ function updatePropGroup(componentName, options) {
|
|
|
780
912
|
},
|
|
781
913
|
}));
|
|
782
914
|
}
|
|
783
|
-
function removePropGroup(
|
|
915
|
+
function removePropGroup(ref, options) {
|
|
784
916
|
const { config, configPath } = loadConfig();
|
|
785
|
-
const component =
|
|
786
|
-
if (!component) {
|
|
787
|
-
console.log(JSON.stringify({
|
|
788
|
-
success: false,
|
|
789
|
-
error: `Component "${componentName}" not found. Available: ${getComponentNames(config).join(", ")}`,
|
|
790
|
-
}));
|
|
791
|
-
process.exit(1);
|
|
792
|
-
}
|
|
917
|
+
const component = resolveComponent(config, ref);
|
|
793
918
|
if (!component.propGroups || component.propGroups.length === 0) {
|
|
794
919
|
console.log(JSON.stringify({
|
|
795
920
|
success: false,
|
|
@@ -825,16 +950,9 @@ function removePropGroup(componentName, options) {
|
|
|
825
950
|
remainingPropGroups: component.propGroups.map(g => g.id),
|
|
826
951
|
}));
|
|
827
952
|
}
|
|
828
|
-
function movePropGroup(
|
|
953
|
+
function movePropGroup(ref, options) {
|
|
829
954
|
const { config, configPath } = loadConfig();
|
|
830
|
-
const component =
|
|
831
|
-
if (!component) {
|
|
832
|
-
console.log(JSON.stringify({
|
|
833
|
-
success: false,
|
|
834
|
-
error: `Component "${componentName}" not found. Available: ${getComponentNames(config).join(", ")}`,
|
|
835
|
-
}));
|
|
836
|
-
process.exit(1);
|
|
837
|
-
}
|
|
955
|
+
const component = resolveComponent(config, ref);
|
|
838
956
|
if (!component.propGroups || component.propGroups.length === 0) {
|
|
839
957
|
console.log(JSON.stringify({
|
|
840
958
|
success: false,
|
|
@@ -857,16 +975,9 @@ function movePropGroup(componentName, options) {
|
|
|
857
975
|
parentGroupId: options.parent || null,
|
|
858
976
|
}));
|
|
859
977
|
}
|
|
860
|
-
function updateComponent(options) {
|
|
978
|
+
function updateComponent(ref, options) {
|
|
861
979
|
const { config, configPath } = loadConfig();
|
|
862
|
-
const component =
|
|
863
|
-
if (!component) {
|
|
864
|
-
console.log(JSON.stringify({
|
|
865
|
-
success: false,
|
|
866
|
-
error: `Component "${options.name}" not found. Available: ${getComponentNames(config).join(", ")}`,
|
|
867
|
-
}));
|
|
868
|
-
process.exit(1);
|
|
869
|
-
}
|
|
980
|
+
const component = resolveComponent(config, ref);
|
|
870
981
|
if (options.isHeader !== undefined) {
|
|
871
982
|
if (options.isHeader) {
|
|
872
983
|
component.isHeader = true;
|
|
@@ -883,20 +994,217 @@ function updateComponent(options) {
|
|
|
883
994
|
delete component.isFooter;
|
|
884
995
|
}
|
|
885
996
|
}
|
|
997
|
+
// displayName is the code-owned default-locale display label (may contain spaces).
|
|
998
|
+
// An empty string clears it (falls back to `name`).
|
|
999
|
+
if (options.displayName !== undefined) {
|
|
1000
|
+
if (options.displayName) {
|
|
1001
|
+
component.displayName = options.displayName;
|
|
1002
|
+
}
|
|
1003
|
+
else {
|
|
1004
|
+
delete component.displayName;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
886
1007
|
saveConfig(configPath, config);
|
|
887
1008
|
console.log(JSON.stringify({
|
|
888
1009
|
success: true,
|
|
889
1010
|
componentName: component.name,
|
|
890
1011
|
type: component.type ?? "component",
|
|
1012
|
+
...(component.displayName ? { displayName: component.displayName } : {}),
|
|
891
1013
|
...(component.isHeader ? { isHeader: true } : {}),
|
|
892
1014
|
...(component.isFooter ? { isFooter: true } : {}),
|
|
893
1015
|
}));
|
|
894
1016
|
}
|
|
1017
|
+
// Upsert a per-locale translation row in `list` by locale, applying only the
|
|
1018
|
+
// provided fields. Studio keeps one translation per locale, so this matches by
|
|
1019
|
+
// locale rather than appending. A row left with no content (all label fields
|
|
1020
|
+
// empty) is not persisted — a new one is skipped, an existing one is pruned —
|
|
1021
|
+
// so we never write a meaningless `{id, locale}` row. Returns the row, or null
|
|
1022
|
+
// when nothing meaningful remains.
|
|
1023
|
+
function upsertTranslation(list, locale, fields) {
|
|
1024
|
+
const existing = list.find((t) => t.locale === locale);
|
|
1025
|
+
const target = existing ?? { id: generateUniqueId(), locale };
|
|
1026
|
+
if (fields.displayName !== undefined)
|
|
1027
|
+
target.displayName = fields.displayName || undefined;
|
|
1028
|
+
if (fields.description !== undefined)
|
|
1029
|
+
target.description = fields.description || undefined;
|
|
1030
|
+
if (fields.name !== undefined)
|
|
1031
|
+
target.name = fields.name || undefined;
|
|
1032
|
+
if (!target.displayName && !target.description && !target.name) {
|
|
1033
|
+
if (existing)
|
|
1034
|
+
list.splice(list.indexOf(existing), 1);
|
|
1035
|
+
return null;
|
|
1036
|
+
}
|
|
1037
|
+
if (!existing)
|
|
1038
|
+
list.push(target);
|
|
1039
|
+
return target;
|
|
1040
|
+
}
|
|
1041
|
+
function setPropTranslation(ref, options) {
|
|
1042
|
+
const { config, configPath } = loadConfig();
|
|
1043
|
+
const component = resolveComponent(config, ref);
|
|
1044
|
+
const prop = component.props.find((p) => p.name === options.prop);
|
|
1045
|
+
if (!prop) {
|
|
1046
|
+
console.log(JSON.stringify({
|
|
1047
|
+
success: false,
|
|
1048
|
+
error: `Prop "${options.prop}" not found on component "${component.name}". Available: ${component.props.map((p) => p.name).join(", ")}`,
|
|
1049
|
+
}));
|
|
1050
|
+
process.exit(1);
|
|
1051
|
+
}
|
|
1052
|
+
if (options.displayName === undefined && options.description === undefined) {
|
|
1053
|
+
console.log(JSON.stringify({
|
|
1054
|
+
success: false,
|
|
1055
|
+
error: "Nothing to set: provide --displayName and/or --description.",
|
|
1056
|
+
}));
|
|
1057
|
+
process.exit(1);
|
|
1058
|
+
}
|
|
1059
|
+
if (!prop.translations)
|
|
1060
|
+
prop.translations = [];
|
|
1061
|
+
const row = upsertTranslation(prop.translations, options.locale, {
|
|
1062
|
+
displayName: options.displayName,
|
|
1063
|
+
description: options.description,
|
|
1064
|
+
});
|
|
1065
|
+
if (prop.translations.length === 0)
|
|
1066
|
+
prop.translations = undefined;
|
|
1067
|
+
saveConfig(configPath, config);
|
|
1068
|
+
console.log(JSON.stringify({
|
|
1069
|
+
success: true,
|
|
1070
|
+
componentName: component.name,
|
|
1071
|
+
prop: prop.name,
|
|
1072
|
+
translation: row,
|
|
1073
|
+
}));
|
|
1074
|
+
}
|
|
1075
|
+
function setGroupTranslation(ref, options) {
|
|
1076
|
+
const { config, configPath } = loadConfig();
|
|
1077
|
+
const component = resolveComponent(config, ref);
|
|
1078
|
+
if (!component.propGroups || component.propGroups.length === 0) {
|
|
1079
|
+
console.log(JSON.stringify({ success: false, error: `No prop groups on component "${component.name}".` }));
|
|
1080
|
+
process.exit(1);
|
|
1081
|
+
}
|
|
1082
|
+
const found = findPropGroup(component.propGroups, options.group);
|
|
1083
|
+
if (!found) {
|
|
1084
|
+
console.log(JSON.stringify({
|
|
1085
|
+
success: false,
|
|
1086
|
+
error: `Prop group "${options.group}" not found on component "${component.name}". Available: ${Array.from(collectPropGroupIds(component.propGroups)).join(", ")}`,
|
|
1087
|
+
}));
|
|
1088
|
+
process.exit(1);
|
|
1089
|
+
}
|
|
1090
|
+
if (options.name === undefined && options.description === undefined) {
|
|
1091
|
+
console.log(JSON.stringify({
|
|
1092
|
+
success: false,
|
|
1093
|
+
error: "Nothing to set: provide --name and/or --description.",
|
|
1094
|
+
}));
|
|
1095
|
+
process.exit(1);
|
|
1096
|
+
}
|
|
1097
|
+
if (!found.group.translations)
|
|
1098
|
+
found.group.translations = [];
|
|
1099
|
+
const row = upsertTranslation(found.group.translations, options.locale, {
|
|
1100
|
+
name: options.name,
|
|
1101
|
+
description: options.description,
|
|
1102
|
+
});
|
|
1103
|
+
if (found.group.translations.length === 0)
|
|
1104
|
+
found.group.translations = undefined;
|
|
1105
|
+
saveConfig(configPath, config);
|
|
1106
|
+
console.log(JSON.stringify({
|
|
1107
|
+
success: true,
|
|
1108
|
+
componentName: component.name,
|
|
1109
|
+
group: found.group.id,
|
|
1110
|
+
translation: row,
|
|
1111
|
+
}));
|
|
1112
|
+
}
|
|
1113
|
+
function removePropTranslation(ref, options) {
|
|
1114
|
+
const { config, configPath } = loadConfig();
|
|
1115
|
+
const component = resolveComponent(config, ref);
|
|
1116
|
+
const prop = component.props.find((p) => p.name === options.prop);
|
|
1117
|
+
if (!prop) {
|
|
1118
|
+
console.log(JSON.stringify({
|
|
1119
|
+
success: false,
|
|
1120
|
+
error: `Prop "${options.prop}" not found on component "${component.name}". Available: ${component.props.map((p) => p.name).join(", ")}`,
|
|
1121
|
+
}));
|
|
1122
|
+
process.exit(1);
|
|
1123
|
+
}
|
|
1124
|
+
const before = prop.translations?.length ?? 0;
|
|
1125
|
+
if (prop.translations) {
|
|
1126
|
+
prop.translations = prop.translations.filter((t) => t.locale !== options.locale);
|
|
1127
|
+
if (prop.translations.length === 0)
|
|
1128
|
+
prop.translations = undefined;
|
|
1129
|
+
}
|
|
1130
|
+
saveConfig(configPath, config);
|
|
1131
|
+
console.log(JSON.stringify({
|
|
1132
|
+
success: true,
|
|
1133
|
+
componentName: component.name,
|
|
1134
|
+
prop: prop.name,
|
|
1135
|
+
removed: before - (prop.translations?.length ?? 0),
|
|
1136
|
+
}));
|
|
1137
|
+
}
|
|
1138
|
+
function removeGroupTranslation(ref, options) {
|
|
1139
|
+
const { config, configPath } = loadConfig();
|
|
1140
|
+
const component = resolveComponent(config, ref);
|
|
1141
|
+
if (!component.propGroups || component.propGroups.length === 0) {
|
|
1142
|
+
console.log(JSON.stringify({ success: false, error: `No prop groups on component "${component.name}".` }));
|
|
1143
|
+
process.exit(1);
|
|
1144
|
+
}
|
|
1145
|
+
const found = findPropGroup(component.propGroups, options.group);
|
|
1146
|
+
if (!found) {
|
|
1147
|
+
console.log(JSON.stringify({
|
|
1148
|
+
success: false,
|
|
1149
|
+
error: `Prop group "${options.group}" not found on component "${component.name}". Available: ${Array.from(collectPropGroupIds(component.propGroups)).join(", ")}`,
|
|
1150
|
+
}));
|
|
1151
|
+
process.exit(1);
|
|
1152
|
+
}
|
|
1153
|
+
const before = found.group.translations?.length ?? 0;
|
|
1154
|
+
if (found.group.translations) {
|
|
1155
|
+
found.group.translations = found.group.translations.filter((t) => t.locale !== options.locale);
|
|
1156
|
+
if (found.group.translations.length === 0)
|
|
1157
|
+
found.group.translations = undefined;
|
|
1158
|
+
}
|
|
1159
|
+
saveConfig(configPath, config);
|
|
1160
|
+
console.log(JSON.stringify({
|
|
1161
|
+
success: true,
|
|
1162
|
+
componentName: component.name,
|
|
1163
|
+
group: found.group.id,
|
|
1164
|
+
removed: before - (found.group.translations?.length ?? 0),
|
|
1165
|
+
}));
|
|
1166
|
+
}
|
|
1167
|
+
function setComponentTranslation(ref, options) {
|
|
1168
|
+
const { config, configPath } = loadConfig();
|
|
1169
|
+
const component = resolveComponent(config, ref);
|
|
1170
|
+
if (options.displayName === undefined) {
|
|
1171
|
+
console.log(JSON.stringify({ success: false, error: "Nothing to set: provide --displayName." }));
|
|
1172
|
+
process.exit(1);
|
|
1173
|
+
}
|
|
1174
|
+
if (!component.translations)
|
|
1175
|
+
component.translations = [];
|
|
1176
|
+
const row = upsertTranslation(component.translations, options.locale, { displayName: options.displayName });
|
|
1177
|
+
if (component.translations.length === 0)
|
|
1178
|
+
component.translations = undefined;
|
|
1179
|
+
saveConfig(configPath, config);
|
|
1180
|
+
console.log(JSON.stringify({
|
|
1181
|
+
success: true,
|
|
1182
|
+
componentName: component.name,
|
|
1183
|
+
translation: row,
|
|
1184
|
+
}));
|
|
1185
|
+
}
|
|
1186
|
+
function removeComponentTranslation(ref, options) {
|
|
1187
|
+
const { config, configPath } = loadConfig();
|
|
1188
|
+
const component = resolveComponent(config, ref);
|
|
1189
|
+
const before = component.translations?.length ?? 0;
|
|
1190
|
+
if (component.translations) {
|
|
1191
|
+
component.translations = component.translations.filter((t) => t.locale !== options.locale);
|
|
1192
|
+
if (component.translations.length === 0)
|
|
1193
|
+
component.translations = undefined;
|
|
1194
|
+
}
|
|
1195
|
+
saveConfig(configPath, config);
|
|
1196
|
+
console.log(JSON.stringify({
|
|
1197
|
+
success: true,
|
|
1198
|
+
componentName: component.name,
|
|
1199
|
+
removed: before - (component.translations?.length ?? 0),
|
|
1200
|
+
}));
|
|
1201
|
+
}
|
|
895
1202
|
function listComponents() {
|
|
896
1203
|
const { config } = loadConfig();
|
|
897
1204
|
const components = config.components.map((c) => ({
|
|
898
1205
|
id: c.id,
|
|
899
1206
|
name: c.name,
|
|
1207
|
+
...(c.displayName ? { displayName: c.displayName } : {}),
|
|
900
1208
|
type: c.type ?? "component",
|
|
901
1209
|
...(c.isHeader ? { isHeader: true } : {}),
|
|
902
1210
|
...(c.isFooter ? { isFooter: true } : {}),
|
|
@@ -1116,17 +1424,20 @@ export function createConfigCommand() {
|
|
|
1116
1424
|
.command("add-component")
|
|
1117
1425
|
.description("Add a new component to the project")
|
|
1118
1426
|
.requiredOption("--name <name>", "Component name (PascalCase or kebab-case)")
|
|
1427
|
+
.option("--displayName <displayName>", "Human-facing display label shown in the editor (may contain spaces; defaults to name)")
|
|
1119
1428
|
.option("--type <type>", "Component type: section or component", "component")
|
|
1120
1429
|
.option("--isHeader", "Mark this section as the store header (only for type: section)")
|
|
1121
1430
|
.option("--isFooter", "Mark this section as the store footer (only for type: section)")
|
|
1122
|
-
.option("--props <json>", "JSON array of props
|
|
1431
|
+
.option("--props <json>", "JSON array of props. Required per entry: name, type. Optional: displayName (auto from name), required, description, defaultValue, groupId, typeId (TYPE props), enumTypeId (ENUM props), filteredComponentIds, privateVarMap. " +
|
|
1432
|
+
"Example: '[{\"name\":\"title\",\"type\":\"TEXT\",\"required\":true,\"defaultValue\":\"Hello\",\"groupId\":\"basic\"}]'")
|
|
1123
1433
|
.action((options) => {
|
|
1124
1434
|
addComponent(options.name, options);
|
|
1125
1435
|
});
|
|
1126
1436
|
config
|
|
1127
1437
|
.command("add-prop")
|
|
1128
1438
|
.description("Add a prop to a component")
|
|
1129
|
-
.
|
|
1439
|
+
.option("--component-id <id>", "Component id (preferred; from `config list-components`)")
|
|
1440
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1130
1441
|
.requiredOption("--name <name>", "Prop name (camelCase)")
|
|
1131
1442
|
.requiredOption("--displayName <displayName>", "Display name in editor")
|
|
1132
1443
|
.requiredOption("--type <type>", `Prop type: ${PROP_TYPES.join(", ")}`)
|
|
@@ -1139,12 +1450,13 @@ export function createConfigCommand() {
|
|
|
1139
1450
|
.option("--filteredComponentIds <json>", "JSON array of component IDs to restrict selection (for COMPONENT/COMPONENT_LIST)")
|
|
1140
1451
|
.option("--privateVarMap <json>", 'JSON object mapping variable keys to {id, typeId} (for COMPONENT/COMPONENT_LIST)')
|
|
1141
1452
|
.action((options) => {
|
|
1142
|
-
addProp(options.component, options);
|
|
1453
|
+
addProp({ id: options.componentId, name: options.component }, options);
|
|
1143
1454
|
});
|
|
1144
1455
|
config
|
|
1145
1456
|
.command("update-prop")
|
|
1146
1457
|
.description("Update a prop on a component")
|
|
1147
|
-
.
|
|
1458
|
+
.option("--component-id <id>", "Component id (preferred; from `config list-components`)")
|
|
1459
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1148
1460
|
.requiredOption("--prop <propName>", "Prop name to update")
|
|
1149
1461
|
.option("--displayName <displayName>", "New display name")
|
|
1150
1462
|
.option("--type <type>", "New prop type")
|
|
@@ -1157,76 +1469,157 @@ export function createConfigCommand() {
|
|
|
1157
1469
|
.option("--filteredComponentIds <json>", "JSON array of component IDs (use 'none' to clear)")
|
|
1158
1470
|
.option("--privateVarMap <json>", "JSON object mapping variable keys to {id, typeId} (use 'none' to clear)")
|
|
1159
1471
|
.action((options) => {
|
|
1160
|
-
updateProp(options.component, options);
|
|
1472
|
+
updateProp({ id: options.componentId, name: options.component }, options);
|
|
1161
1473
|
});
|
|
1162
1474
|
config
|
|
1163
1475
|
.command("remove-prop")
|
|
1164
1476
|
.description("Remove a prop from a component")
|
|
1165
|
-
.
|
|
1477
|
+
.option("--component-id <id>", "Component id (preferred; from `config list-components`)")
|
|
1478
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1166
1479
|
.requiredOption("--prop <propName>", "Prop name to remove")
|
|
1167
1480
|
.action((options) => {
|
|
1168
|
-
removeProp(options.component, options);
|
|
1481
|
+
removeProp({ id: options.componentId, name: options.component }, options);
|
|
1169
1482
|
});
|
|
1170
1483
|
config
|
|
1171
1484
|
.command("update-component")
|
|
1172
|
-
.description("Update a component's metadata (isHeader, isFooter)")
|
|
1173
|
-
.
|
|
1485
|
+
.description("Update a component's metadata (displayName, isHeader, isFooter). Identify by --id (preferred) or exact --name.")
|
|
1486
|
+
.option("--id <id>", "Component id (preferred; from `config list-components`)")
|
|
1487
|
+
.option("--name <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1488
|
+
.option("--displayName <displayName>", "Human-facing display label shown in the editor (may contain spaces; empty string clears it)")
|
|
1174
1489
|
.option("--isHeader", "Mark as store header")
|
|
1175
1490
|
.option("--no-isHeader", "Unmark as store header")
|
|
1176
1491
|
.option("--isFooter", "Mark as store footer")
|
|
1177
1492
|
.option("--no-isFooter", "Unmark as store footer")
|
|
1178
1493
|
.action((options) => {
|
|
1179
|
-
updateComponent(options);
|
|
1494
|
+
updateComponent({ id: options.id, name: options.name }, options);
|
|
1180
1495
|
});
|
|
1181
1496
|
config
|
|
1182
1497
|
.command("remove-component")
|
|
1183
|
-
.description("Remove a component from the project (deletes files)")
|
|
1184
|
-
.
|
|
1498
|
+
.description("Remove a component from the project (deletes files). Identify by --id (preferred) or exact --name.")
|
|
1499
|
+
.option("--id <id>", "Component id (preferred; from `config list-components`)")
|
|
1500
|
+
.option("--name <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1185
1501
|
.action((options) => {
|
|
1186
|
-
removeComponent(options);
|
|
1502
|
+
removeComponent({ id: options.id, name: options.name });
|
|
1187
1503
|
});
|
|
1188
1504
|
config
|
|
1189
1505
|
.command("add-prop-group")
|
|
1190
1506
|
.description("Add a prop group to a component")
|
|
1191
|
-
.
|
|
1507
|
+
.option("--component-id <componentId>", "Component id (preferred; from `config list-components`)")
|
|
1508
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1192
1509
|
.requiredOption("--id <id>", "Group ID (unique within component, kebab-case)")
|
|
1193
1510
|
.requiredOption("--name <name>", "Display name in editor")
|
|
1194
1511
|
.option("--description <description>", "Group description")
|
|
1195
1512
|
.option("--parent <parentId>", "Parent group ID (for nesting, max 1 level)")
|
|
1196
1513
|
.action((options) => {
|
|
1197
|
-
addPropGroup(options.component, options);
|
|
1514
|
+
addPropGroup({ id: options.componentId, name: options.component }, options);
|
|
1198
1515
|
});
|
|
1199
1516
|
config
|
|
1200
1517
|
.command("update-prop-group")
|
|
1201
1518
|
.description("Update a prop group on a component")
|
|
1202
|
-
.
|
|
1519
|
+
.option("--component-id <componentId>", "Component id (preferred; from `config list-components`)")
|
|
1520
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1203
1521
|
.requiredOption("--id <id>", "Group ID to update")
|
|
1204
1522
|
.option("--name <name>", "New display name")
|
|
1205
1523
|
.option("--description <description>", "New description")
|
|
1206
1524
|
.action((options) => {
|
|
1207
|
-
updatePropGroup(options.component, options);
|
|
1525
|
+
updatePropGroup({ id: options.componentId, name: options.component }, options);
|
|
1208
1526
|
});
|
|
1209
1527
|
config
|
|
1210
1528
|
.command("remove-prop-group")
|
|
1211
1529
|
.description("Remove a prop group from a component (props become ungrouped)")
|
|
1212
|
-
.
|
|
1530
|
+
.option("--component-id <componentId>", "Component id (preferred; from `config list-components`)")
|
|
1531
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1213
1532
|
.requiredOption("--id <id>", "Group ID to remove")
|
|
1214
1533
|
.action((options) => {
|
|
1215
|
-
removePropGroup(options.component, options);
|
|
1534
|
+
removePropGroup({ id: options.componentId, name: options.component }, options);
|
|
1216
1535
|
});
|
|
1217
1536
|
config
|
|
1218
1537
|
.command("move-prop-group")
|
|
1219
1538
|
.description("Move a prop group to a different parent or position (for drag-and-drop reordering)")
|
|
1220
|
-
.
|
|
1539
|
+
.option("--component-id <componentId>", "Component id (preferred; from `config list-components`)")
|
|
1540
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1221
1541
|
.requiredOption("--id <id>", "Group ID to move")
|
|
1222
1542
|
.option("--parent <parentId>", "Target parent group ID (omit to move to root)")
|
|
1223
1543
|
.option("--index <index>", "Zero-based insertion index within the target parent (appends when omitted)", v => parseInt(v, 10))
|
|
1224
1544
|
.action((options) => {
|
|
1225
|
-
movePropGroup(options.component, options);
|
|
1545
|
+
movePropGroup({ id: options.componentId, name: options.component }, options);
|
|
1546
|
+
});
|
|
1547
|
+
config
|
|
1548
|
+
.command("set-prop-translation")
|
|
1549
|
+
.description("Add or update a per-locale translation for a prop's label/description. " +
|
|
1550
|
+
"Seeded into the editor on first import of a new component or a newly added prop; " +
|
|
1551
|
+
"studio-owned translations for already-imported props are not overwritten.")
|
|
1552
|
+
.option("--component-id <id>", "Component id (preferred; from `config list-components`)")
|
|
1553
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1554
|
+
.requiredOption("--prop <propName>", "Prop name to translate")
|
|
1555
|
+
.requiredOption("--locale <locale>", "Target locale (e.g. de, fr, en)")
|
|
1556
|
+
.option("--displayName <text>", "Translated display name")
|
|
1557
|
+
.option("--description <text>", "Translated description")
|
|
1558
|
+
.action((options) => {
|
|
1559
|
+
setPropTranslation({ id: options.componentId, name: options.component }, options);
|
|
1560
|
+
});
|
|
1561
|
+
config
|
|
1562
|
+
.command("set-group-translation")
|
|
1563
|
+
.description("Add or update a per-locale translation for a prop group's name/description. " +
|
|
1564
|
+
"Seeded into the editor only for groups studio has not seen yet (studio wins otherwise).")
|
|
1565
|
+
.option("--component-id <id>", "Component id (preferred; from `config list-components`)")
|
|
1566
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1567
|
+
.requiredOption("--group <groupId>", "Prop group id (local, as in ikas.config.json)")
|
|
1568
|
+
.requiredOption("--locale <locale>", "Target locale (e.g. de, fr, en)")
|
|
1569
|
+
.option("--name <text>", "Translated group name")
|
|
1570
|
+
.option("--description <text>", "Translated description")
|
|
1571
|
+
.action((options) => {
|
|
1572
|
+
setGroupTranslation({ id: options.componentId, name: options.component }, options);
|
|
1573
|
+
});
|
|
1574
|
+
config
|
|
1575
|
+
.command("remove-prop-translation")
|
|
1576
|
+
.description("Remove a prop's translation for a given locale")
|
|
1577
|
+
.option("--component-id <id>", "Component id (preferred; from `config list-components`)")
|
|
1578
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1579
|
+
.requiredOption("--prop <propName>", "Prop name")
|
|
1580
|
+
.requiredOption("--locale <locale>", "Locale to remove")
|
|
1581
|
+
.action((options) => {
|
|
1582
|
+
removePropTranslation({ id: options.componentId, name: options.component }, options);
|
|
1583
|
+
});
|
|
1584
|
+
config
|
|
1585
|
+
.command("remove-group-translation")
|
|
1586
|
+
.description("Remove a prop group's translation for a given locale")
|
|
1587
|
+
.option("--component-id <id>", "Component id (preferred; from `config list-components`)")
|
|
1588
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1589
|
+
.requiredOption("--group <groupId>", "Prop group id (local, as in ikas.config.json)")
|
|
1590
|
+
.requiredOption("--locale <locale>", "Locale to remove")
|
|
1591
|
+
.action((options) => {
|
|
1592
|
+
removeGroupTranslation({ id: options.componentId, name: options.component }, options);
|
|
1593
|
+
});
|
|
1594
|
+
config
|
|
1595
|
+
.command("set-component-translation")
|
|
1596
|
+
.description("Add or update a per-locale translation for a section/component's own display label. " +
|
|
1597
|
+
"Seeded into the editor only for components studio has not seen yet (studio wins otherwise).")
|
|
1598
|
+
.option("--component-id <id>", "Component id (preferred; from `config list-components`)")
|
|
1599
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1600
|
+
.requiredOption("--locale <locale>", "Target locale (e.g. de, fr, en)")
|
|
1601
|
+
.option("--displayName <text>", "Translated component display label")
|
|
1602
|
+
.action((options) => {
|
|
1603
|
+
setComponentTranslation({ id: options.componentId, name: options.component }, options);
|
|
1604
|
+
});
|
|
1605
|
+
config
|
|
1606
|
+
.command("remove-component-translation")
|
|
1607
|
+
.description("Remove a section/component name translation for a given locale")
|
|
1608
|
+
.option("--component-id <id>", "Component id (preferred; from `config list-components`)")
|
|
1609
|
+
.option("--component <name>", "Component name — exact match (PascalCase, as stored in ikas.config.json)")
|
|
1610
|
+
.requiredOption("--locale <locale>", "Locale to remove")
|
|
1611
|
+
.action((options) => {
|
|
1612
|
+
removeComponentTranslation({ id: options.componentId, name: options.component }, options);
|
|
1613
|
+
});
|
|
1614
|
+
config
|
|
1615
|
+
.command("list-components")
|
|
1616
|
+
.description("List all components and their props (with canonical ids for use with --id flags)")
|
|
1617
|
+
.action(() => {
|
|
1618
|
+
listComponents();
|
|
1226
1619
|
});
|
|
1227
1620
|
config
|
|
1228
1621
|
.command("list")
|
|
1229
|
-
.description("
|
|
1622
|
+
.description("Alias for `list-components` (kept for backwards compatibility)")
|
|
1230
1623
|
.action(() => {
|
|
1231
1624
|
listComponents();
|
|
1232
1625
|
});
|
|
@@ -1237,6 +1630,13 @@ export function createConfigCommand() {
|
|
|
1237
1630
|
.action(async (options) => {
|
|
1238
1631
|
await listTypes(options.componentType);
|
|
1239
1632
|
});
|
|
1633
|
+
config
|
|
1634
|
+
.command("list-locales")
|
|
1635
|
+
.description("List the editor's configured locales — the ONLY valid --locale values for set-prop-translation / set-group-translation. Run this before adding translations; never invent a locale. (Requires a running dev server with the editor connected.)")
|
|
1636
|
+
.option("--port <port>", "Dev server WebSocket port", "5201")
|
|
1637
|
+
.action(async (options) => {
|
|
1638
|
+
await listLocales(options.port ? parseInt(options.port, 10) : undefined);
|
|
1639
|
+
});
|
|
1240
1640
|
config
|
|
1241
1641
|
.command("add-enum")
|
|
1242
1642
|
.description("Create a custom enum type (offline, no editor needed)")
|