@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete-theme-globals.js","sourceRoot":"","sources":["../../src/commands/delete-theme-globals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAE1G,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;AAErF,SAAS,QAAQ,CAAC,IAAa;IAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChD,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,iCAAiC;IAC/C,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAClD,GAAG;SACA,WAAW,CAAC,oDAAoD,CAAC;SACjE,cAAc,CAAC,cAAc,EAAE,wDAAwD,CAAC;SACxF,MAAM,CAAC,eAAe,EAAE,2BAA2B,EAAE,MAAM,CAAC;SAC5D,MAAM,CAAC,KAAK,EAAE,OAAyC,EAAE,EAAE;QAC1D,IAAI,CAAC,OAAO,CAAC,IAAI;YAAE,iBAAiB,CAAC,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;QACtE,IAAI,CAAC;YACH,kBAAkB,CAAC,MAAM,eAAe,CAAC,wBAAwB,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,iBAAiB,CAAC,CAAC,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;IACL,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,8BAA8B;IAC5C,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC/C,GAAG;SACA,WAAW,CAAC,4GAA4G,CAAC;SACzH,cAAc,CAAC,qBAAqB,EAAE,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;SAC9D,cAAc,CAAC,WAAW,EAAE,0CAA0C,CAAC;SACvE,MAAM,CAAC,eAAe,EAAE,2BAA2B,EAAE,MAAM,CAAC;SAC5D,MAAM,CAAC,KAAK,EAAE,OAA2D,EAAE,EAAE;QAC5E,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACnE,iBAAiB,CAAC,IAAI,KAAK,CAAC,gCAAgC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACzF,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,iBAAiB,CAAC,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC;YACH,kBAAkB,CAChB,MAAM,eAAe,CACnB,qBAAqB,EACrB,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAChD,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CACvB,CACF,CAAC;QACJ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,iBAAiB,CAAC,CAAC,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;IACL,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAysDpC,wBAAgB,gBAAgB,IAAI,OAAO,CAI1C"}
|
package/dist/commands/dev.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import chokidar from "chokidar";
|
|
3
3
|
import { Command } from "commander";
|
|
4
|
+
import * as crypto from "crypto";
|
|
4
5
|
import * as fs from "fs";
|
|
5
6
|
import * as path from "path";
|
|
6
|
-
import { toPascalCase, generateTypesFile, generateGlobalTypesFile, collectUsedEnumIds, collectUsedCustomTypeIds, generateComponentFile, generateStylesFile, updateBarrelExport, generateProjectId, generateComponentId, generateUniqueId, buildPropGroupHierarchy, findPropGroup, collectPropGroupIds, movePropGroupInTree, validateFilteredComponentIds, } from "../utils/component-helpers.js";
|
|
7
|
+
import { toPascalCase, generateTypesFile, generateGlobalTypesFile, collectUsedEnumIds, collectUsedCustomTypeIds, generateComponentFile, generateStylesFile, updateBarrelExport, generateProjectId, generateComponentId, generateUniqueId, buildPropGroupHierarchy, findPropGroup, collectPropGroupIds, movePropGroupInTree, validateFilteredComponentIds, diffConfigTranslations, } from "../utils/component-helpers.js";
|
|
7
8
|
import { compileAllComponents as compileAllComponentsMulti } from "../utils/compile.js";
|
|
8
9
|
import { transformEsmToWindowGlobals, transformChunkForCanvas, buildChunkMapping, } from "../utils/esm-transform.js";
|
|
9
10
|
import { DevServerWebSocket, } from "../utils/websocket-server.js";
|
|
@@ -12,6 +13,13 @@ const DEV_SERVER_PORT = 5201;
|
|
|
12
13
|
const compiledComponents = new Map();
|
|
13
14
|
const compiledSharedModules = new Map();
|
|
14
15
|
let compiledGlobalStyles;
|
|
16
|
+
// Hashes broadcast on the last cycle, used to compute deltas. Reset only on process
|
|
17
|
+
// startup. Editors compare these hashes (sent on the wire as `contentHash`) against their
|
|
18
|
+
// stored YJS value to decide whether to apply the update — so once we've seen a given hash
|
|
19
|
+
// here, we can safely skip broadcasting it again.
|
|
20
|
+
const lastBroadcastComponentHashes = new Map();
|
|
21
|
+
const lastBroadcastSharedHashes = new Map();
|
|
22
|
+
let lastBroadcastGlobalStyleHash;
|
|
15
23
|
// Cached types list received from the editor via sync-types
|
|
16
24
|
let cachedEditorTypes = null;
|
|
17
25
|
// Cached custom type definitions received from the editor via sync-types (full IType objects)
|
|
@@ -128,12 +136,23 @@ async function compileAllComponentsForDev(config) {
|
|
|
128
136
|
if (chunkIds.length > 0) {
|
|
129
137
|
console.log(chalk.cyan(` Extracted ${chunkIds.length} shared chunk(s)`));
|
|
130
138
|
}
|
|
131
|
-
// Store global CSS (unscoped)
|
|
139
|
+
// Store global CSS (unscoped). Preserve `updatedAt` across rebuilds when the compiled
|
|
140
|
+
// global CSS is byte-identical so the editor only sees a fresh timestamp when the
|
|
141
|
+
// designer actually changed `global.css` — the publish UI watches this timestamp to
|
|
142
|
+
// decide whether globals need (re)publishing independently of any code component.
|
|
132
143
|
if (result.globalCss && config.projectId) {
|
|
133
|
-
|
|
144
|
+
const previousHash = computeGlobalStyleHash(compiledGlobalStyles);
|
|
145
|
+
const previousUpdatedAt = compiledGlobalStyles?.updatedAt;
|
|
146
|
+
const next = {
|
|
134
147
|
projectId: config.projectId,
|
|
135
148
|
compiledCss: result.globalCss,
|
|
136
149
|
};
|
|
150
|
+
const nextHash = computeGlobalStyleHash(next);
|
|
151
|
+
next.updatedAt =
|
|
152
|
+
nextHash === previousHash && previousUpdatedAt !== undefined
|
|
153
|
+
? previousUpdatedAt
|
|
154
|
+
: Date.now();
|
|
155
|
+
compiledGlobalStyles = next;
|
|
137
156
|
console.log(chalk.cyan(` Compiled global styles`));
|
|
138
157
|
}
|
|
139
158
|
else {
|
|
@@ -172,6 +191,7 @@ async function compileAllComponentsForDev(config) {
|
|
|
172
191
|
...(prop.filteredComponentIds
|
|
173
192
|
? { filteredComponentIds: prop.filteredComponentIds }
|
|
174
193
|
: {}),
|
|
194
|
+
...(prop.translations ? { translations: prop.translations } : {}),
|
|
175
195
|
}));
|
|
176
196
|
// Build prop group hierarchy if groups are defined
|
|
177
197
|
const propGroups = component.propGroups && component.propGroups.length > 0
|
|
@@ -185,7 +205,9 @@ async function compileAllComponentsForDev(config) {
|
|
|
185
205
|
const data = {
|
|
186
206
|
id: component.id,
|
|
187
207
|
name: component.name,
|
|
208
|
+
...(component.displayName ? { displayName: component.displayName } : {}),
|
|
188
209
|
projectId: config.projectId,
|
|
210
|
+
...(component.translations ? { translations: component.translations } : {}),
|
|
189
211
|
props: propsWithIds,
|
|
190
212
|
compiledServerJs: entry.serverJs,
|
|
191
213
|
compiledClientJs: entry.clientJs,
|
|
@@ -208,6 +230,7 @@ async function compileAllComponentsForDev(config) {
|
|
|
208
230
|
compiledComponents.set(component.id, data);
|
|
209
231
|
console.log(chalk.green(` Compiled: ${component.name}`));
|
|
210
232
|
}
|
|
233
|
+
populateContentHashes();
|
|
211
234
|
return true;
|
|
212
235
|
}
|
|
213
236
|
/**
|
|
@@ -248,6 +271,7 @@ function rebuildCompiledComponentFromConfig(config, componentId) {
|
|
|
248
271
|
...(prop.filteredComponentIds
|
|
249
272
|
? { filteredComponentIds: prop.filteredComponentIds }
|
|
250
273
|
: {}),
|
|
274
|
+
...(prop.translations ? { translations: prop.translations } : {}),
|
|
251
275
|
}));
|
|
252
276
|
const propGroups = component.propGroups && component.propGroups.length > 0
|
|
253
277
|
? buildPropGroupHierarchy(component.id, component.propGroups, propsWithIds)
|
|
@@ -257,6 +281,8 @@ function rebuildCompiledComponentFromConfig(config, componentId) {
|
|
|
257
281
|
: undefined;
|
|
258
282
|
const updated = {
|
|
259
283
|
...existing,
|
|
284
|
+
displayName: component.displayName,
|
|
285
|
+
translations: component.translations,
|
|
260
286
|
props: propsWithIds,
|
|
261
287
|
propGroups,
|
|
262
288
|
configPropGroups: component.propGroups && component.propGroups.length > 0
|
|
@@ -267,6 +293,7 @@ function rebuildCompiledComponentFromConfig(config, componentId) {
|
|
|
267
293
|
: undefined,
|
|
268
294
|
};
|
|
269
295
|
compiledComponents.set(componentId, updated);
|
|
296
|
+
populateContentHashes();
|
|
270
297
|
return updated;
|
|
271
298
|
}
|
|
272
299
|
/**
|
|
@@ -275,6 +302,156 @@ function rebuildCompiledComponentFromConfig(config, componentId) {
|
|
|
275
302
|
function getAllSharedModules() {
|
|
276
303
|
return Array.from(compiledSharedModules.values());
|
|
277
304
|
}
|
|
305
|
+
function sha256Hex(input) {
|
|
306
|
+
return crypto.createHash("sha256").update(input).digest("hex");
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Compute `contentHash` for every entry in `compiledSharedModules` and `compiledComponents`,
|
|
310
|
+
* mutating each entry in place. A component's hash incorporates the hashes of its referenced
|
|
311
|
+
* shared modules (sorted), so any shared-util change cascades into consumers without
|
|
312
|
+
* requiring a separate "shared module changed" message — the consumer's own hash already
|
|
313
|
+
* differs. Global styles are tracked separately via `updatedAt` on the global style payload;
|
|
314
|
+
* they intentionally do NOT roll into component hashes (designers publish globals as a
|
|
315
|
+
* standalone unit).
|
|
316
|
+
*/
|
|
317
|
+
function populateContentHashes() {
|
|
318
|
+
for (const mod of compiledSharedModules.values()) {
|
|
319
|
+
mod.contentHash = sha256Hex([mod.id, mod.compiledServerJs, mod.compiledClientJs, mod.canvasClientJs, mod.compiledCss].join("\0"));
|
|
320
|
+
}
|
|
321
|
+
for (const comp of compiledComponents.values()) {
|
|
322
|
+
const referencedHashes = (comp.usedSharedModuleIds || [])
|
|
323
|
+
.slice()
|
|
324
|
+
.sort()
|
|
325
|
+
.map((id) => `${id}:${compiledSharedModules.get(id)?.contentHash || ""}`)
|
|
326
|
+
.join("|");
|
|
327
|
+
comp.contentHash = sha256Hex([
|
|
328
|
+
comp.id,
|
|
329
|
+
comp.name,
|
|
330
|
+
// displayName is code-owned (a display variant of name). Hash it so a
|
|
331
|
+
// displayName-only config edit bumps the hash, triggering a component
|
|
332
|
+
// broadcast and passing the editor's autoSyncToTheme contentHash gate.
|
|
333
|
+
comp.displayName || "",
|
|
334
|
+
comp.projectId || "",
|
|
335
|
+
comp.type || "component",
|
|
336
|
+
comp.isHeader ? "1" : "0",
|
|
337
|
+
comp.isFooter ? "1" : "0",
|
|
338
|
+
comp.compiledServerJs,
|
|
339
|
+
comp.compiledClientJs,
|
|
340
|
+
comp.compiledCss,
|
|
341
|
+
comp.canvasClientJs,
|
|
342
|
+
JSON.stringify(comp.props),
|
|
343
|
+
JSON.stringify(comp.propGroups || null),
|
|
344
|
+
JSON.stringify(comp.configPropGroups || null),
|
|
345
|
+
JSON.stringify(comp.customTypes || null),
|
|
346
|
+
(comp.usedSharedModuleIds || []).slice().sort().join(","),
|
|
347
|
+
referencedHashes,
|
|
348
|
+
].join("\0"));
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function computeGlobalStyleHash(g) {
|
|
352
|
+
if (!g)
|
|
353
|
+
return undefined;
|
|
354
|
+
return sha256Hex([g.projectId, g.compiledCss].join("\0"));
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Build a `component-update` payload's `sharedModules` array for one component: the chunks
|
|
358
|
+
* the editor needs to resolve this component's imports. We always include the full
|
|
359
|
+
* transitive set (cheap) so the editor never sees a missing chunk during a partial update.
|
|
360
|
+
*/
|
|
361
|
+
function sharedModulesForComponent(comp) {
|
|
362
|
+
if (!comp.usedSharedModuleIds || comp.usedSharedModuleIds.length === 0)
|
|
363
|
+
return undefined;
|
|
364
|
+
const out = [];
|
|
365
|
+
for (const id of comp.usedSharedModuleIds) {
|
|
366
|
+
const mod = compiledSharedModules.get(id);
|
|
367
|
+
if (mod)
|
|
368
|
+
out.push(mod);
|
|
369
|
+
}
|
|
370
|
+
return out.length > 0 ? out : undefined;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Diff current compile state against `lastBroadcast*` maps and decide what to send:
|
|
374
|
+
*
|
|
375
|
+
* - Nothing changed → no broadcast at all (this is the whole point — quiet rebuilds).
|
|
376
|
+
* - Exactly one component changed, no deletes, no global change → single
|
|
377
|
+
* `component-update` (cheap, no full-state replay on the editor).
|
|
378
|
+
* - Anything more (multi-component, deletes, global style change) → one bundled
|
|
379
|
+
* `initial-state`. We deliberately collapse multi-component changes into ONE message so
|
|
380
|
+
* the editor doesn't tick through N sequential `markUpdating` / refresh cycles. The
|
|
381
|
+
* editor's `autoSyncToTheme` uses `contentHash` to skip YJS writes for components that
|
|
382
|
+
* didn't actually change, so the full-state payload only mutates what really moved.
|
|
383
|
+
*
|
|
384
|
+
* Newly-connecting editors always receive a full `initial-state` via `onRequestState` in
|
|
385
|
+
* websocket-server.ts and are independent of this path.
|
|
386
|
+
*/
|
|
387
|
+
function broadcastCompileDelta(wsServer) {
|
|
388
|
+
const currentGlobalHash = computeGlobalStyleHash(compiledGlobalStyles);
|
|
389
|
+
const globalStylesChanged = currentGlobalHash !== lastBroadcastGlobalStyleHash;
|
|
390
|
+
const changedComponentIds = [];
|
|
391
|
+
for (const [id, comp] of compiledComponents) {
|
|
392
|
+
const prev = lastBroadcastComponentHashes.get(id);
|
|
393
|
+
if (prev !== comp.contentHash)
|
|
394
|
+
changedComponentIds.push(id);
|
|
395
|
+
}
|
|
396
|
+
const deletedComponentIds = [];
|
|
397
|
+
for (const id of lastBroadcastComponentHashes.keys()) {
|
|
398
|
+
if (!compiledComponents.has(id))
|
|
399
|
+
deletedComponentIds.push(id);
|
|
400
|
+
}
|
|
401
|
+
let changedSharedCount = 0;
|
|
402
|
+
for (const [id, mod] of compiledSharedModules) {
|
|
403
|
+
if (lastBroadcastSharedHashes.get(id) !== mod.contentHash)
|
|
404
|
+
changedSharedCount++;
|
|
405
|
+
}
|
|
406
|
+
const result = {
|
|
407
|
+
changedComponents: changedComponentIds.length,
|
|
408
|
+
deletedComponents: deletedComponentIds.length,
|
|
409
|
+
changedSharedModules: changedSharedCount,
|
|
410
|
+
globalStylesChanged,
|
|
411
|
+
};
|
|
412
|
+
const nothingChanged = changedComponentIds.length === 0 &&
|
|
413
|
+
deletedComponentIds.length === 0 &&
|
|
414
|
+
!globalStylesChanged;
|
|
415
|
+
if (nothingChanged)
|
|
416
|
+
return result;
|
|
417
|
+
// Single-component edit fast path: avoid making the editor clear+refill its devComponents
|
|
418
|
+
// map and run autoSyncToTheme for every component. One targeted update is enough.
|
|
419
|
+
const canUseSingleUpdate = changedComponentIds.length === 1 &&
|
|
420
|
+
deletedComponentIds.length === 0 &&
|
|
421
|
+
!globalStylesChanged;
|
|
422
|
+
if (canUseSingleUpdate) {
|
|
423
|
+
const comp = compiledComponents.get(changedComponentIds[0]);
|
|
424
|
+
wsServer.broadcastComponentUpdate(comp, sharedModulesForComponent(comp));
|
|
425
|
+
lastBroadcastComponentHashes.set(comp.id, comp.contentHash);
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
for (const id of deletedComponentIds) {
|
|
429
|
+
wsServer.broadcastComponentDeleted(id);
|
|
430
|
+
lastBroadcastComponentHashes.delete(id);
|
|
431
|
+
}
|
|
432
|
+
wsServer.broadcast({
|
|
433
|
+
type: "initial-state",
|
|
434
|
+
payload: {
|
|
435
|
+
components: getAllComponents(),
|
|
436
|
+
sharedModules: getAllSharedModules(),
|
|
437
|
+
globalStyles: compiledGlobalStyles,
|
|
438
|
+
timestamp: Date.now(),
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
lastBroadcastComponentHashes.clear();
|
|
442
|
+
for (const [id, comp] of compiledComponents) {
|
|
443
|
+
if (comp.contentHash)
|
|
444
|
+
lastBroadcastComponentHashes.set(id, comp.contentHash);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
lastBroadcastSharedHashes.clear();
|
|
448
|
+
for (const [id, mod] of compiledSharedModules) {
|
|
449
|
+
if (mod.contentHash)
|
|
450
|
+
lastBroadcastSharedHashes.set(id, mod.contentHash);
|
|
451
|
+
}
|
|
452
|
+
lastBroadcastGlobalStyleHash = currentGlobalHash;
|
|
453
|
+
return result;
|
|
454
|
+
}
|
|
278
455
|
/**
|
|
279
456
|
* Read and return the current config from disk
|
|
280
457
|
*/
|
|
@@ -323,7 +500,10 @@ function createEditorHandlers(configPath, getConfig, setConfig, wsServer) {
|
|
|
323
500
|
const broadcastConfigChange = (componentId) => {
|
|
324
501
|
const updated = rebuildCompiledComponentFromConfig(getConfig(), componentId);
|
|
325
502
|
if (updated) {
|
|
326
|
-
wsServer.broadcastComponentUpdate(updated);
|
|
503
|
+
wsServer.broadcastComponentUpdate(updated, sharedModulesForComponent(updated));
|
|
504
|
+
if (updated.contentHash) {
|
|
505
|
+
lastBroadcastComponentHashes.set(updated.id, updated.contentHash);
|
|
506
|
+
}
|
|
327
507
|
}
|
|
328
508
|
};
|
|
329
509
|
return {
|
|
@@ -441,6 +621,37 @@ function createEditorHandlers(configPath, getConfig, setConfig, wsServer) {
|
|
|
441
621
|
writeGlobalTypesFile(config);
|
|
442
622
|
console.log(chalk.green(` Updated prop "${propName}" on ${component.name}`));
|
|
443
623
|
},
|
|
624
|
+
onUpdatePropLocalization: async (componentId, propName, updates) => {
|
|
625
|
+
const config = getConfig();
|
|
626
|
+
const component = config.components.find((c) => c.id === componentId);
|
|
627
|
+
if (!component) {
|
|
628
|
+
throw new Error(`Component not found: ${componentId}`);
|
|
629
|
+
}
|
|
630
|
+
const prop = component.props.find((p) => p.name === propName);
|
|
631
|
+
if (!prop) {
|
|
632
|
+
throw new Error(`Prop "${propName}" not found on ${component.name}`);
|
|
633
|
+
}
|
|
634
|
+
// Patch ONLY studio-owned label fields; structural fields stay untouched.
|
|
635
|
+
const previousDescription = prop.description;
|
|
636
|
+
if (updates.displayName !== undefined)
|
|
637
|
+
prop.displayName = updates.displayName;
|
|
638
|
+
if (updates.description !== undefined)
|
|
639
|
+
prop.description = updates.description || undefined;
|
|
640
|
+
if (updates.translations !== undefined) {
|
|
641
|
+
prop.translations = updates.translations.length > 0 ? updates.translations : undefined;
|
|
642
|
+
}
|
|
643
|
+
writeConfig(configPath, config);
|
|
644
|
+
setConfig(config);
|
|
645
|
+
// `description` is the only label field that reaches generated types.ts (as a
|
|
646
|
+
// JSDoc comment); displayName/translations never affect TS output. Regenerate
|
|
647
|
+
// only when it actually changed to avoid fs churn on every label edit.
|
|
648
|
+
if (prop.description !== previousDescription) {
|
|
649
|
+
const componentDir = path.resolve(process.cwd(), path.dirname(component.entry));
|
|
650
|
+
fs.writeFileSync(path.join(componentDir, "types.ts"), generateTypesFile(component.name, component.props, component.type, cachedEditorTypes, cachedCustomTypes));
|
|
651
|
+
writeGlobalTypesFile(config);
|
|
652
|
+
}
|
|
653
|
+
console.log(chalk.green(` Updated prop localization "${propName}" on ${component.name}`));
|
|
654
|
+
},
|
|
444
655
|
onDeleteProp: async (componentId, propName) => {
|
|
445
656
|
const config = getConfig();
|
|
446
657
|
const component = config.components.find((c) => c.id === componentId);
|
|
@@ -511,11 +722,25 @@ function createEditorHandlers(configPath, getConfig, setConfig, wsServer) {
|
|
|
511
722
|
found.group.name = updates.name;
|
|
512
723
|
if (updates.description !== undefined)
|
|
513
724
|
found.group.description = updates.description || undefined;
|
|
725
|
+
if (updates.translations !== undefined)
|
|
726
|
+
found.group.translations = updates.translations.length > 0 ? updates.translations : undefined;
|
|
514
727
|
writeConfig(configPath, config);
|
|
515
728
|
setConfig(config);
|
|
516
729
|
broadcastConfigChange(componentId);
|
|
517
730
|
console.log(chalk.green(` Updated prop group "${propGroupId}" on ${component.name}`));
|
|
518
731
|
},
|
|
732
|
+
onUpdateComponentLocalization: async (componentId, updates) => {
|
|
733
|
+
const config = getConfig();
|
|
734
|
+
const component = config.components.find((c) => c.id === componentId);
|
|
735
|
+
if (!component)
|
|
736
|
+
throw new Error(`Component not found: ${componentId}`);
|
|
737
|
+
if (updates.translations !== undefined)
|
|
738
|
+
component.translations = updates.translations.length > 0 ? updates.translations : undefined;
|
|
739
|
+
writeConfig(configPath, config);
|
|
740
|
+
setConfig(config);
|
|
741
|
+
broadcastConfigChange(componentId);
|
|
742
|
+
console.log(chalk.green(` Updated display-label translations on ${component.name}`));
|
|
743
|
+
},
|
|
519
744
|
onDeletePropGroup: async (componentId, propGroupId) => {
|
|
520
745
|
const config = getConfig();
|
|
521
746
|
const component = config.components.find((c) => c.id === componentId);
|
|
@@ -808,6 +1033,18 @@ async function startDevServer(opts = {}) {
|
|
|
808
1033
|
// Initial compilation of all components (multi-entry with code splitting)
|
|
809
1034
|
console.log(chalk.cyan(" Compiling components..."));
|
|
810
1035
|
await compileAllComponentsForDev(config);
|
|
1036
|
+
// Seed the broadcast-hash maps so the first delta-broadcast after startup doesn't replay
|
|
1037
|
+
// the initial state — newly-connecting editors get a full `initial-state` directly via
|
|
1038
|
+
// `onRequestState`, independent of the delta path.
|
|
1039
|
+
for (const [id, comp] of compiledComponents) {
|
|
1040
|
+
if (comp.contentHash)
|
|
1041
|
+
lastBroadcastComponentHashes.set(id, comp.contentHash);
|
|
1042
|
+
}
|
|
1043
|
+
for (const [id, mod] of compiledSharedModules) {
|
|
1044
|
+
if (mod.contentHash)
|
|
1045
|
+
lastBroadcastSharedHashes.set(id, mod.contentHash);
|
|
1046
|
+
}
|
|
1047
|
+
lastBroadcastGlobalStyleHash = computeGlobalStyleHash(compiledGlobalStyles);
|
|
811
1048
|
console.log("");
|
|
812
1049
|
// Start WebSocket server
|
|
813
1050
|
const wsServer = new DevServerWebSocket(DEV_SERVER_PORT);
|
|
@@ -848,16 +1085,15 @@ async function startDevServer(opts = {}) {
|
|
|
848
1085
|
console.log(chalk.yellow(` Recompiling all (triggered by: ${triggeredBy})...`));
|
|
849
1086
|
const success = await compileAllComponentsForDev(config);
|
|
850
1087
|
if (success) {
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
});
|
|
1088
|
+
const delta = broadcastCompileDelta(wsServer);
|
|
1089
|
+
if (delta.changedComponents === 0 &&
|
|
1090
|
+
delta.deletedComponents === 0 &&
|
|
1091
|
+
!delta.globalStylesChanged) {
|
|
1092
|
+
console.log(chalk.gray(" No editor-visible changes (skipped broadcast)"));
|
|
1093
|
+
}
|
|
1094
|
+
else {
|
|
1095
|
+
console.log(chalk.cyan(` Broadcast delta: ${delta.changedComponents} changed, ${delta.deletedComponents} deleted, ${delta.changedSharedModules} shared chunks changed${delta.globalStylesChanged ? ", globals updated" : ""}`));
|
|
1096
|
+
}
|
|
861
1097
|
}
|
|
862
1098
|
else {
|
|
863
1099
|
// Broadcast error for the triggering component if identifiable
|
|
@@ -874,20 +1110,39 @@ async function startDevServer(opts = {}) {
|
|
|
874
1110
|
if (normalizedPath === configPath) {
|
|
875
1111
|
console.log(chalk.yellow("\n Config changed, reloading..."));
|
|
876
1112
|
try {
|
|
877
|
-
|
|
1113
|
+
const prevConfig = config;
|
|
1114
|
+
const newConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
1115
|
+
// Authoritative live translation push: detect external translation edits
|
|
1116
|
+
// (e.g. an agent running `config set-prop-translation`) and apply them in the
|
|
1117
|
+
// editor, overriding studio for that (entity, locale) only. Diffing against
|
|
1118
|
+
// the in-memory config naturally suppresses echoes from the editor's own
|
|
1119
|
+
// reverse-sync writes (those already updated in-memory, so prev == new).
|
|
1120
|
+
const translationChanges = diffConfigTranslations(prevConfig.components, newConfig.components);
|
|
1121
|
+
for (const change of translationChanges) {
|
|
1122
|
+
if (change.kind === "prop") {
|
|
1123
|
+
wsServer.broadcastApplyPropTranslation(change.componentId, change.propName, change.locale, change.translation);
|
|
1124
|
+
}
|
|
1125
|
+
else if (change.kind === "group") {
|
|
1126
|
+
wsServer.broadcastApplyPropGroupTranslation(change.componentId, change.propGroupId, change.locale, change.translation);
|
|
1127
|
+
}
|
|
1128
|
+
else {
|
|
1129
|
+
wsServer.broadcastApplyComponentTranslation(change.componentId, change.locale, change.translation);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
// Point-of-need guidance: this fires whether the change came from the
|
|
1133
|
+
// `config *-translation` commands or a manual edit. It confirms the change
|
|
1134
|
+
// synced (so it won't be "overwritten" by the editor) and names the canonical
|
|
1135
|
+
// commands — independent of whether CLAUDE.md / docs are up to date.
|
|
1136
|
+
if (translationChanges.length > 0) {
|
|
1137
|
+
console.log(chalk.cyan(` ${translationChanges.length} translation change(s) pushed to the connected editor (live, authoritative).`));
|
|
1138
|
+
console.log(chalk.gray(" Manage label translations via the CLI (run `config --help`): config set-prop-translation / set-group-translation / remove-*-translation."));
|
|
1139
|
+
}
|
|
1140
|
+
config = newConfig;
|
|
878
1141
|
cachedCustomTypes = config.customTypes || [];
|
|
879
1142
|
writeGlobalTypesFile(config);
|
|
880
1143
|
await compileAllComponentsForDev(config);
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
type: "initial-state",
|
|
884
|
-
payload: {
|
|
885
|
-
components: getAllComponents(),
|
|
886
|
-
sharedModules: getAllSharedModules(),
|
|
887
|
-
globalStyles: compiledGlobalStyles,
|
|
888
|
-
timestamp: Date.now(),
|
|
889
|
-
},
|
|
890
|
-
});
|
|
1144
|
+
const delta = broadcastCompileDelta(wsServer);
|
|
1145
|
+
console.log(chalk.cyan(` Config reload delta: ${delta.changedComponents} changed, ${delta.deletedComponents} deleted, ${delta.changedSharedModules} shared chunks changed${delta.globalStylesChanged ? ", globals updated" : ""}`));
|
|
891
1146
|
}
|
|
892
1147
|
catch (e) {
|
|
893
1148
|
console.error(chalk.red(" Failed to reload config"));
|
|
@@ -914,6 +1169,23 @@ async function startDevServer(opts = {}) {
|
|
|
914
1169
|
const entryPath = path.resolve(process.cwd(), component.entry);
|
|
915
1170
|
if (!fs.existsSync(entryPath)) {
|
|
916
1171
|
compiledComponents.delete(component.id);
|
|
1172
|
+
// Sync ikas.config.json: remove the component entry and strip any orphaned
|
|
1173
|
+
// filteredComponentIds references in remaining components' props. Write config
|
|
1174
|
+
// before broadcasting so an editor requesting state sees the post-delete view.
|
|
1175
|
+
const idx = config.components.findIndex((c) => c.id === component.id);
|
|
1176
|
+
if (idx !== -1) {
|
|
1177
|
+
config.components.splice(idx, 1);
|
|
1178
|
+
for (const remaining of config.components) {
|
|
1179
|
+
for (const prop of remaining.props || []) {
|
|
1180
|
+
if (!prop.filteredComponentIds)
|
|
1181
|
+
continue;
|
|
1182
|
+
prop.filteredComponentIds = prop.filteredComponentIds.filter((id) => id !== component.id);
|
|
1183
|
+
if (prop.filteredComponentIds.length === 0)
|
|
1184
|
+
delete prop.filteredComponentIds;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
writeConfig(configPath, config);
|
|
1188
|
+
}
|
|
917
1189
|
wsServer.broadcastComponentDeleted(component.id);
|
|
918
1190
|
console.log(chalk.red(` Component removed: ${component.name}`));
|
|
919
1191
|
}
|