@ikas/component-cli 1.4.0-beta.7 → 1.4.0-beta.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/commands/add-sections-to-page.d.ts +3 -0
  2. package/dist/commands/add-sections-to-page.d.ts.map +1 -0
  3. package/dist/commands/add-sections-to-page.js +39 -0
  4. package/dist/commands/add-sections-to-page.js.map +1 -0
  5. package/dist/commands/add-to-page.d.ts +3 -0
  6. package/dist/commands/add-to-page.d.ts.map +1 -0
  7. package/dist/commands/add-to-page.js +41 -0
  8. package/dist/commands/add-to-page.js.map +1 -0
  9. package/dist/commands/build.d.ts.map +1 -1
  10. package/dist/commands/build.js +5 -165
  11. package/dist/commands/build.js.map +1 -1
  12. package/dist/commands/config.d.ts.map +1 -1
  13. package/dist/commands/config.js +212 -113
  14. package/dist/commands/config.js.map +1 -1
  15. package/dist/commands/create-design-tokens.d.ts +9 -0
  16. package/dist/commands/create-design-tokens.d.ts.map +1 -0
  17. package/dist/commands/create-design-tokens.js +178 -0
  18. package/dist/commands/create-design-tokens.js.map +1 -0
  19. package/dist/commands/create-global-variable.d.ts +3 -0
  20. package/dist/commands/create-global-variable.d.ts.map +1 -0
  21. package/dist/commands/create-global-variable.js +53 -0
  22. package/dist/commands/create-global-variable.js.map +1 -0
  23. package/dist/commands/create-page.d.ts +3 -0
  24. package/dist/commands/create-page.d.ts.map +1 -0
  25. package/dist/commands/create-page.js +31 -0
  26. package/dist/commands/create-page.js.map +1 -0
  27. package/dist/commands/create.d.ts +3 -8
  28. package/dist/commands/create.d.ts.map +1 -1
  29. package/dist/commands/create.js +175 -8
  30. package/dist/commands/create.js.map +1 -1
  31. package/dist/commands/delete-theme-globals.d.ts +4 -0
  32. package/dist/commands/delete-theme-globals.d.ts.map +1 -0
  33. package/dist/commands/delete-theme-globals.js +48 -0
  34. package/dist/commands/delete-theme-globals.js.map +1 -0
  35. package/dist/commands/dev.d.ts.map +1 -1
  36. package/dist/commands/dev.js +217 -31
  37. package/dist/commands/dev.js.map +1 -1
  38. package/dist/commands/get-component-props.d.ts +3 -0
  39. package/dist/commands/get-component-props.d.ts.map +1 -0
  40. package/dist/commands/get-component-props.js +32 -0
  41. package/dist/commands/get-component-props.js.map +1 -0
  42. package/dist/commands/get-page-by-type.d.ts +3 -0
  43. package/dist/commands/get-page-by-type.d.ts.map +1 -0
  44. package/dist/commands/get-page-by-type.js +25 -0
  45. package/dist/commands/get-page-by-type.js.map +1 -0
  46. package/dist/commands/get-section-values.d.ts +3 -0
  47. package/dist/commands/get-section-values.d.ts.map +1 -0
  48. package/dist/commands/get-section-values.js +39 -0
  49. package/dist/commands/get-section-values.js.map +1 -0
  50. package/dist/commands/import.d.ts +3 -0
  51. package/dist/commands/import.d.ts.map +1 -0
  52. package/dist/commands/import.js +25 -0
  53. package/dist/commands/import.js.map +1 -0
  54. package/dist/commands/list-entities.d.ts +3 -0
  55. package/dist/commands/list-entities.d.ts.map +1 -0
  56. package/dist/commands/list-entities.js +32 -0
  57. package/dist/commands/list-entities.js.map +1 -0
  58. package/dist/commands/list-imported.d.ts +3 -0
  59. package/dist/commands/list-imported.d.ts.map +1 -0
  60. package/dist/commands/list-imported.js +25 -0
  61. package/dist/commands/list-imported.js.map +1 -0
  62. package/dist/commands/list-page-sections.d.ts +3 -0
  63. package/dist/commands/list-page-sections.d.ts.map +1 -0
  64. package/dist/commands/list-page-sections.js +25 -0
  65. package/dist/commands/list-page-sections.js.map +1 -0
  66. package/dist/commands/list-pages.d.ts +3 -0
  67. package/dist/commands/list-pages.d.ts.map +1 -0
  68. package/dist/commands/list-pages.js +21 -0
  69. package/dist/commands/list-pages.js.map +1 -0
  70. package/dist/commands/list-theme-globals.d.ts +3 -0
  71. package/dist/commands/list-theme-globals.d.ts.map +1 -0
  72. package/dist/commands/list-theme-globals.js +22 -0
  73. package/dist/commands/list-theme-globals.js.map +1 -0
  74. package/dist/commands/publish-theme.d.ts +3 -0
  75. package/dist/commands/publish-theme.d.ts.map +1 -0
  76. package/dist/commands/publish-theme.js +29 -0
  77. package/dist/commands/publish-theme.js.map +1 -0
  78. package/dist/commands/search-products.d.ts +3 -0
  79. package/dist/commands/search-products.d.ts.map +1 -0
  80. package/dist/commands/search-products.js +40 -0
  81. package/dist/commands/search-products.js.map +1 -0
  82. package/dist/commands/update-global-variable.d.ts +3 -0
  83. package/dist/commands/update-global-variable.d.ts.map +1 -0
  84. package/dist/commands/update-global-variable.js +47 -0
  85. package/dist/commands/update-global-variable.js.map +1 -0
  86. package/dist/commands/update-page-sections.d.ts +3 -0
  87. package/dist/commands/update-page-sections.d.ts.map +1 -0
  88. package/dist/commands/update-page-sections.js +39 -0
  89. package/dist/commands/update-page-sections.js.map +1 -0
  90. package/dist/commands/update-section-prop.d.ts +3 -0
  91. package/dist/commands/update-section-prop.d.ts.map +1 -0
  92. package/dist/commands/update-section-prop.js +59 -0
  93. package/dist/commands/update-section-prop.js.map +1 -0
  94. package/dist/commands/upload-image.d.ts +3 -0
  95. package/dist/commands/upload-image.d.ts.map +1 -0
  96. package/dist/commands/upload-image.js +38 -0
  97. package/dist/commands/upload-image.js.map +1 -0
  98. package/dist/commands/upload-images.d.ts +3 -0
  99. package/dist/commands/upload-images.d.ts.map +1 -0
  100. package/dist/commands/upload-images.js +48 -0
  101. package/dist/commands/upload-images.js.map +1 -0
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +50 -0
  104. package/dist/index.js.map +1 -1
  105. package/dist/types.d.ts +11 -1
  106. package/dist/types.d.ts.map +1 -1
  107. package/dist/utils/compile.d.ts +4 -1
  108. package/dist/utils/compile.d.ts.map +1 -1
  109. package/dist/utils/compile.js +517 -48
  110. package/dist/utils/compile.js.map +1 -1
  111. package/dist/utils/component-helpers.d.ts +21 -3
  112. package/dist/utils/component-helpers.d.ts.map +1 -1
  113. package/dist/utils/component-helpers.js +111 -4
  114. package/dist/utils/component-helpers.js.map +1 -1
  115. package/dist/utils/editor-action-client.d.ts +28 -0
  116. package/dist/utils/editor-action-client.d.ts.map +1 -0
  117. package/dist/utils/editor-action-client.js +116 -0
  118. package/dist/utils/editor-action-client.js.map +1 -0
  119. package/dist/utils/load-image.d.ts +16 -0
  120. package/dist/utils/load-image.d.ts.map +1 -0
  121. package/dist/utils/load-image.js +50 -0
  122. package/dist/utils/load-image.js.map +1 -0
  123. package/dist/utils/websocket-server.d.ts +52 -2
  124. package/dist/utils/websocket-server.d.ts.map +1 -1
  125. package/dist/utils/websocket-server.js +59 -1
  126. package/dist/utils/websocket-server.js.map +1 -1
  127. package/package.json +1 -1
  128. package/dist/commands/proxy.d.ts +0 -39
  129. package/dist/commands/proxy.d.ts.map +0 -1
  130. package/dist/commands/proxy.js +0 -212
  131. package/dist/commands/proxy.js.map +0 -1
@@ -1,9 +1,176 @@
1
- import { createCreateCommand as createCreateCommandImpl } from "create-ikas-component";
2
- /**
3
- * Re-export of the `create` command. The actual implementation (and the
4
- * project templates) live in the standalone `create-ikas-component` package
5
- * so users can run `npx create-ikas-component` without pulling in the full
6
- * dev/build/publish CLI.
7
- */
8
- export const createCreateCommand = createCreateCommandImpl;
1
+ import chalk from "chalk";
2
+ import { Command } from "commander";
3
+ import * as fs from "fs";
4
+ import inquirer from "inquirer";
5
+ import ora from "ora";
6
+ import * as path from "path";
7
+ import { generateComponentId, generateProjectId } from "../utils/component-helpers.js";
8
+ import { readTemplate } from "../utils/template.js";
9
+ const PROP_TYPES = [
10
+ "TEXT",
11
+ "NUMBER",
12
+ "BOOLEAN",
13
+ "IMAGE",
14
+ "LINK",
15
+ "LIST_OF_LINK",
16
+ "COLOR",
17
+ "PRODUCT",
18
+ "PRODUCT_LIST",
19
+ "CATEGORY",
20
+ "CATEGORY_LIST",
21
+ "BRAND",
22
+ "BRAND_LIST",
23
+ "BLOG",
24
+ "BLOG_LIST",
25
+ "BLOG_CATEGORY",
26
+ "BLOG_CATEGORY_LIST",
27
+ "TYPE",
28
+ "ENUM",
29
+ "COMPONENT",
30
+ "COMPONENT_LIST",
31
+ ];
32
+ async function createProject(projectName) {
33
+ const spinner = ora("Creating project...").start();
34
+ try {
35
+ const projectPath = path.resolve(process.cwd(), projectName);
36
+ // Check if directory exists
37
+ if (fs.existsSync(projectPath)) {
38
+ spinner.fail(`Directory "${projectName}" already exists`);
39
+ process.exit(1);
40
+ }
41
+ // Create directory structure
42
+ fs.mkdirSync(projectPath, { recursive: true });
43
+ fs.mkdirSync(path.join(projectPath, "src/components/ExampleComponent"), { recursive: true });
44
+ fs.mkdirSync(path.join(projectPath, "src/components/ExampleSection"), { recursive: true });
45
+ // Generate IDs for the example config
46
+ const projectId = generateProjectId();
47
+ const exampleComponentId = generateComponentId(projectId, "example-component");
48
+ const exampleSectionId = generateComponentId(projectId, "example-section");
49
+ const vars = {
50
+ PROJECT_NAME: projectName,
51
+ PROJECT_ID: projectId,
52
+ EXAMPLE_COMPONENT_ID: exampleComponentId,
53
+ EXAMPLE_SECTION_ID: exampleSectionId,
54
+ };
55
+ // Write all template files
56
+ const files = [
57
+ { template: "create/package.json", dest: "package.json", vars },
58
+ { template: "create/tsconfig.json", dest: "tsconfig.json" },
59
+ { template: "create/vite.config.ts", dest: "vite.config.ts" },
60
+ { template: "create/ikas.config.json", dest: "ikas.config.json", vars },
61
+ { template: "create/gitignore", dest: ".gitignore" },
62
+ { template: "create/mcp.json", dest: ".mcp.json" },
63
+ { template: "create/README.md", dest: "README.md", vars },
64
+ { template: "create/claude-md", dest: "CLAUDE.md" },
65
+ { template: "create/cursorrules", dest: ".cursorrules" },
66
+ { template: "create/src/global.css", dest: "src/global.css" },
67
+ { template: "create/src/global-types.ts", dest: "src/global-types.ts" },
68
+ { template: "create/src/ikas-component-utils.d.ts", dest: "src/ikas-component-utils.d.ts" },
69
+ { template: "create/src/components/index.ts", dest: "src/components/index.ts" },
70
+ { template: "create/src/components/ExampleComponent/index.tsx", dest: "src/components/ExampleComponent/index.tsx" },
71
+ { template: "create/src/components/ExampleComponent/types.ts", dest: "src/components/ExampleComponent/types.ts" },
72
+ { template: "create/src/components/ExampleComponent/styles.css", dest: "src/components/ExampleComponent/styles.css" },
73
+ { template: "create/src/components/ExampleSection/index.tsx", dest: "src/components/ExampleSection/index.tsx" },
74
+ { template: "create/src/components/ExampleSection/types.ts", dest: "src/components/ExampleSection/types.ts" },
75
+ { template: "create/src/components/ExampleSection/styles.css", dest: "src/components/ExampleSection/styles.css" },
76
+ ];
77
+ for (const file of files) {
78
+ fs.writeFileSync(path.join(projectPath, file.dest), readTemplate(file.template, file.vars));
79
+ }
80
+ // Create or merge .cursor/mcp.json for Cursor editor
81
+ // Uses node + ${workspaceFolder} instead of npx because Cursor
82
+ // doesn't reliably inherit the shell PATH.
83
+ const cursorDir = path.join(projectPath, ".cursor");
84
+ const cursorMcpPath = path.join(cursorDir, "mcp.json");
85
+ fs.mkdirSync(cursorDir, { recursive: true });
86
+ let cursorMcpConfig = { mcpServers: {} };
87
+ if (fs.existsSync(cursorMcpPath)) {
88
+ try {
89
+ cursorMcpConfig = JSON.parse(fs.readFileSync(cursorMcpPath, "utf-8"));
90
+ if (!cursorMcpConfig.mcpServers) {
91
+ cursorMcpConfig.mcpServers = {};
92
+ }
93
+ }
94
+ catch {
95
+ cursorMcpConfig = { mcpServers: {} };
96
+ }
97
+ }
98
+ cursorMcpConfig.mcpServers["ikas-code-components"] = {
99
+ command: "node",
100
+ args: ["${workspaceFolder}/node_modules/@ikas/code-components-mcp/dist/index.js"]
101
+ };
102
+ fs.writeFileSync(cursorMcpPath, JSON.stringify(cursorMcpConfig, null, 2));
103
+ spinner.succeed(chalk.green(`Project "${projectName}" created successfully!`));
104
+ console.log("\n" + chalk.cyan("Next steps:"));
105
+ console.log(chalk.white(` cd ${projectName}`));
106
+ console.log(chalk.white(" npm install"));
107
+ console.log(chalk.white(" npm run dev"));
108
+ console.log("\n" + chalk.gray("Happy coding! 🚀"));
109
+ }
110
+ catch (error) {
111
+ spinner.fail("Failed to create project");
112
+ console.error(error);
113
+ process.exit(1);
114
+ }
115
+ }
116
+ export function createCreateCommand() {
117
+ return new Command("create")
118
+ .description("Create a new ikas component project")
119
+ .argument("[name]", "Project name")
120
+ .action(async (name) => {
121
+ let projectName = name;
122
+ if (!projectName) {
123
+ const answers = await inquirer.prompt([
124
+ {
125
+ type: "input",
126
+ name: "projectName",
127
+ message: "What is your project name?",
128
+ default: "my-ikas-components",
129
+ validate: (input) => {
130
+ if (!input.trim())
131
+ return "Project name is required";
132
+ if (!/^[a-z0-9-_]+$/i.test(input)) {
133
+ return "Project name can only contain letters, numbers, hyphens, and underscores";
134
+ }
135
+ return true;
136
+ }
137
+ }
138
+ ]);
139
+ projectName = answers.projectName;
140
+ }
141
+ await createProject(projectName);
142
+ });
143
+ }
144
+ // Standalone runner for create-ikas-component command
145
+ export async function run() {
146
+ const program = new Command();
147
+ program
148
+ .name("create-ikas-component")
149
+ .description("Create a new ikas component project")
150
+ .argument("[name]", "Project name")
151
+ .action(async (name) => {
152
+ let projectName = name;
153
+ if (!projectName) {
154
+ const answers = await inquirer.prompt([
155
+ {
156
+ type: "input",
157
+ name: "projectName",
158
+ message: "What is your project name?",
159
+ default: "my-ikas-components",
160
+ validate: (input) => {
161
+ if (!input.trim())
162
+ return "Project name is required";
163
+ if (!/^[a-z0-9-_]+$/i.test(input)) {
164
+ return "Project name can only contain letters, numbers, hyphens, and underscores";
165
+ }
166
+ return true;
167
+ }
168
+ }
169
+ ]);
170
+ projectName = answers.projectName;
171
+ }
172
+ await createProject(projectName);
173
+ });
174
+ program.parse(process.argv);
175
+ }
9
176
  //# sourceMappingURL=create.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,IAAI,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEvF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,uBAAuB,CAAC"}
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Command } from "commander";
2
+ export declare function createDeleteGlobalVariableCommand(): Command;
3
+ export declare function createDeleteDesignTokenCommand(): Command;
4
+ //# sourceMappingURL=delete-theme-globals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-theme-globals.d.ts","sourceRoot":"","sources":["../../src/commands/delete-theme-globals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,iCAAiC,IAAI,OAAO,CAe3D;AAED,wBAAgB,8BAA8B,IAAI,OAAO,CAyBxD"}
@@ -0,0 +1,48 @@
1
+ import { Command } from "commander";
2
+ import { printErrorAndExit, printResultAndExit, runEditorAction } from "../utils/editor-action-client.js";
3
+ const TOKEN_TYPES = ["color", "typography", "breakpoint", "keyframe", "colorScheme"];
4
+ function portOpts(port) {
5
+ const p = port ? parseInt(port, 10) : undefined;
6
+ return p ? { port: p } : {};
7
+ }
8
+ export function createDeleteGlobalVariableCommand() {
9
+ const cmd = new Command("delete-global-variable");
10
+ cmd
11
+ .description("Delete a global variable from the connected editor")
12
+ .requiredOption("--name <key>", "The variable's runtime key (from `list-theme-globals`)")
13
+ .option("--port <port>", "Dev server WebSocket port", "5201")
14
+ .action(async (options) => {
15
+ if (!options.name)
16
+ printErrorAndExit(new Error("--name is required"));
17
+ try {
18
+ printResultAndExit(await runEditorAction("delete-global-variable", { name: options.name }, portOpts(options.port)));
19
+ }
20
+ catch (e) {
21
+ printErrorAndExit(e);
22
+ }
23
+ });
24
+ return cmd;
25
+ }
26
+ export function createDeleteDesignTokenCommand() {
27
+ const cmd = new Command("delete-design-token");
28
+ cmd
29
+ .description("Delete a design token (color, typography, breakpoint, keyframe, or color scheme) from the connected editor")
30
+ .requiredOption("--token-type <type>", TOKEN_TYPES.join(" | "))
31
+ .requiredOption("--id <id>", "The token id (from `list-theme-globals`)")
32
+ .option("--port <port>", "Dev server WebSocket port", "5201")
33
+ .action(async (options) => {
34
+ if (!options.tokenType || !TOKEN_TYPES.includes(options.tokenType)) {
35
+ printErrorAndExit(new Error(`--token-type must be one of: ${TOKEN_TYPES.join(", ")}`));
36
+ }
37
+ if (!options.id)
38
+ printErrorAndExit(new Error("--id is required"));
39
+ try {
40
+ printResultAndExit(await runEditorAction("delete-design-token", { tokenType: options.tokenType, id: options.id }, portOpts(options.port)));
41
+ }
42
+ catch (e) {
43
+ printErrorAndExit(e);
44
+ }
45
+ });
46
+ return cmd;
47
+ }
48
+ //# sourceMappingURL=delete-theme-globals.js.map
@@ -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;AA24CpC,wBAAgB,gBAAgB,IAAI,OAAO,CAI1C"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkmDpC,wBAAgB,gBAAgB,IAAI,OAAO,CAI1C"}
@@ -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, shouldWriteGlobalTypes, generateComponentFile, generateStylesFile, updateBarrelExport, generateProjectId, generateComponentId, generateUniqueId, buildPropGroupHierarchy, findPropGroup, collectPropGroupIds, movePropGroupInTree, validateFilteredComponentIds, } 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,10 +13,20 @@ 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)
18
26
  let cachedCustomTypes = null;
27
+ // Cached theme global variables received from the editor via sync-types.
28
+ // Drives the ThemeSettingName union in src/global-types.ts.
29
+ let cachedThemeSettings = null;
19
30
  const LIBRARY_TYPE_PREFIX = "@ikas/";
20
31
  /**
21
32
  * Match custom types for a component's props from the cached custom types.
@@ -128,12 +139,23 @@ async function compileAllComponentsForDev(config) {
128
139
  if (chunkIds.length > 0) {
129
140
  console.log(chalk.cyan(` Extracted ${chunkIds.length} shared chunk(s)`));
130
141
  }
131
- // Store global CSS (unscoped)
142
+ // Store global CSS (unscoped). Preserve `updatedAt` across rebuilds when the compiled
143
+ // global CSS is byte-identical so the editor only sees a fresh timestamp when the
144
+ // designer actually changed `global.css` — the publish UI watches this timestamp to
145
+ // decide whether globals need (re)publishing independently of any code component.
132
146
  if (result.globalCss && config.projectId) {
133
- compiledGlobalStyles = {
147
+ const previousHash = computeGlobalStyleHash(compiledGlobalStyles);
148
+ const previousUpdatedAt = compiledGlobalStyles?.updatedAt;
149
+ const next = {
134
150
  projectId: config.projectId,
135
151
  compiledCss: result.globalCss,
136
152
  };
153
+ const nextHash = computeGlobalStyleHash(next);
154
+ next.updatedAt =
155
+ nextHash === previousHash && previousUpdatedAt !== undefined
156
+ ? previousUpdatedAt
157
+ : Date.now();
158
+ compiledGlobalStyles = next;
137
159
  console.log(chalk.cyan(` Compiled global styles`));
138
160
  }
139
161
  else {
@@ -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
  /**
@@ -267,6 +290,7 @@ function rebuildCompiledComponentFromConfig(config, componentId) {
267
290
  : undefined,
268
291
  };
269
292
  compiledComponents.set(componentId, updated);
293
+ populateContentHashes();
270
294
  return updated;
271
295
  }
272
296
  /**
@@ -275,6 +299,152 @@ function rebuildCompiledComponentFromConfig(config, componentId) {
275
299
  function getAllSharedModules() {
276
300
  return Array.from(compiledSharedModules.values());
277
301
  }
302
+ function sha256Hex(input) {
303
+ return crypto.createHash("sha256").update(input).digest("hex");
304
+ }
305
+ /**
306
+ * Compute `contentHash` for every entry in `compiledSharedModules` and `compiledComponents`,
307
+ * mutating each entry in place. A component's hash incorporates the hashes of its referenced
308
+ * shared modules (sorted), so any shared-util change cascades into consumers without
309
+ * requiring a separate "shared module changed" message — the consumer's own hash already
310
+ * differs. Global styles are tracked separately via `updatedAt` on the global style payload;
311
+ * they intentionally do NOT roll into component hashes (designers publish globals as a
312
+ * standalone unit).
313
+ */
314
+ function populateContentHashes() {
315
+ for (const mod of compiledSharedModules.values()) {
316
+ mod.contentHash = sha256Hex([mod.id, mod.compiledServerJs, mod.compiledClientJs, mod.canvasClientJs, mod.compiledCss].join("\0"));
317
+ }
318
+ for (const comp of compiledComponents.values()) {
319
+ const referencedHashes = (comp.usedSharedModuleIds || [])
320
+ .slice()
321
+ .sort()
322
+ .map((id) => `${id}:${compiledSharedModules.get(id)?.contentHash || ""}`)
323
+ .join("|");
324
+ comp.contentHash = sha256Hex([
325
+ comp.id,
326
+ comp.name,
327
+ comp.projectId || "",
328
+ comp.type || "component",
329
+ comp.isHeader ? "1" : "0",
330
+ comp.isFooter ? "1" : "0",
331
+ comp.compiledServerJs,
332
+ comp.compiledClientJs,
333
+ comp.compiledCss,
334
+ comp.canvasClientJs,
335
+ JSON.stringify(comp.props),
336
+ JSON.stringify(comp.propGroups || null),
337
+ JSON.stringify(comp.configPropGroups || null),
338
+ JSON.stringify(comp.customTypes || null),
339
+ (comp.usedSharedModuleIds || []).slice().sort().join(","),
340
+ referencedHashes,
341
+ ].join("\0"));
342
+ }
343
+ }
344
+ function computeGlobalStyleHash(g) {
345
+ if (!g)
346
+ return undefined;
347
+ return sha256Hex([g.projectId, g.compiledCss].join("\0"));
348
+ }
349
+ /**
350
+ * Build a `component-update` payload's `sharedModules` array for one component: the chunks
351
+ * the editor needs to resolve this component's imports. We always include the full
352
+ * transitive set (cheap) so the editor never sees a missing chunk during a partial update.
353
+ */
354
+ function sharedModulesForComponent(comp) {
355
+ if (!comp.usedSharedModuleIds || comp.usedSharedModuleIds.length === 0)
356
+ return undefined;
357
+ const out = [];
358
+ for (const id of comp.usedSharedModuleIds) {
359
+ const mod = compiledSharedModules.get(id);
360
+ if (mod)
361
+ out.push(mod);
362
+ }
363
+ return out.length > 0 ? out : undefined;
364
+ }
365
+ /**
366
+ * Diff current compile state against `lastBroadcast*` maps and decide what to send:
367
+ *
368
+ * - Nothing changed → no broadcast at all (this is the whole point — quiet rebuilds).
369
+ * - Exactly one component changed, no deletes, no global change → single
370
+ * `component-update` (cheap, no full-state replay on the editor).
371
+ * - Anything more (multi-component, deletes, global style change) → one bundled
372
+ * `initial-state`. We deliberately collapse multi-component changes into ONE message so
373
+ * the editor doesn't tick through N sequential `markUpdating` / refresh cycles. The
374
+ * editor's `autoSyncToTheme` uses `contentHash` to skip YJS writes for components that
375
+ * didn't actually change, so the full-state payload only mutates what really moved.
376
+ *
377
+ * Newly-connecting editors always receive a full `initial-state` via `onRequestState` in
378
+ * websocket-server.ts and are independent of this path.
379
+ */
380
+ function broadcastCompileDelta(wsServer) {
381
+ const currentGlobalHash = computeGlobalStyleHash(compiledGlobalStyles);
382
+ const globalStylesChanged = currentGlobalHash !== lastBroadcastGlobalStyleHash;
383
+ const changedComponentIds = [];
384
+ for (const [id, comp] of compiledComponents) {
385
+ const prev = lastBroadcastComponentHashes.get(id);
386
+ if (prev !== comp.contentHash)
387
+ changedComponentIds.push(id);
388
+ }
389
+ const deletedComponentIds = [];
390
+ for (const id of lastBroadcastComponentHashes.keys()) {
391
+ if (!compiledComponents.has(id))
392
+ deletedComponentIds.push(id);
393
+ }
394
+ let changedSharedCount = 0;
395
+ for (const [id, mod] of compiledSharedModules) {
396
+ if (lastBroadcastSharedHashes.get(id) !== mod.contentHash)
397
+ changedSharedCount++;
398
+ }
399
+ const result = {
400
+ changedComponents: changedComponentIds.length,
401
+ deletedComponents: deletedComponentIds.length,
402
+ changedSharedModules: changedSharedCount,
403
+ globalStylesChanged,
404
+ };
405
+ const nothingChanged = changedComponentIds.length === 0 &&
406
+ deletedComponentIds.length === 0 &&
407
+ !globalStylesChanged;
408
+ if (nothingChanged)
409
+ return result;
410
+ // Single-component edit fast path: avoid making the editor clear+refill its devComponents
411
+ // map and run autoSyncToTheme for every component. One targeted update is enough.
412
+ const canUseSingleUpdate = changedComponentIds.length === 1 &&
413
+ deletedComponentIds.length === 0 &&
414
+ !globalStylesChanged;
415
+ if (canUseSingleUpdate) {
416
+ const comp = compiledComponents.get(changedComponentIds[0]);
417
+ wsServer.broadcastComponentUpdate(comp, sharedModulesForComponent(comp));
418
+ lastBroadcastComponentHashes.set(comp.id, comp.contentHash);
419
+ }
420
+ else {
421
+ for (const id of deletedComponentIds) {
422
+ wsServer.broadcastComponentDeleted(id);
423
+ lastBroadcastComponentHashes.delete(id);
424
+ }
425
+ wsServer.broadcast({
426
+ type: "initial-state",
427
+ payload: {
428
+ components: getAllComponents(),
429
+ sharedModules: getAllSharedModules(),
430
+ globalStyles: compiledGlobalStyles,
431
+ timestamp: Date.now(),
432
+ },
433
+ });
434
+ lastBroadcastComponentHashes.clear();
435
+ for (const [id, comp] of compiledComponents) {
436
+ if (comp.contentHash)
437
+ lastBroadcastComponentHashes.set(id, comp.contentHash);
438
+ }
439
+ }
440
+ lastBroadcastSharedHashes.clear();
441
+ for (const [id, mod] of compiledSharedModules) {
442
+ if (mod.contentHash)
443
+ lastBroadcastSharedHashes.set(id, mod.contentHash);
444
+ }
445
+ lastBroadcastGlobalStyleHash = currentGlobalHash;
446
+ return result;
447
+ }
278
448
  /**
279
449
  * Read and return the current config from disk
280
450
  */
@@ -294,12 +464,20 @@ function writeConfig(configPath, config) {
294
464
  function writeGlobalTypesFile(config) {
295
465
  const globalTypesPath = path.resolve(process.cwd(), "src/global-types.ts");
296
466
  const usedEnumIds = collectUsedEnumIds(config.components);
297
- const content = generateGlobalTypesFile(cachedEditorTypes, cachedCustomTypes, usedEnumIds);
298
- // Avoid unnecessary writes (prevents file watcher loops)
299
- if (fs.existsSync(globalTypesPath)) {
300
- const existing = fs.readFileSync(globalTypesPath, "utf-8");
301
- if (existing === content)
302
- return;
467
+ const content = generateGlobalTypesFile(cachedEditorTypes, cachedCustomTypes, usedEnumIds, cachedThemeSettings);
468
+ // Single read instead of existsSync + readFileSync; absent file reads as null.
469
+ let existing = null;
470
+ try {
471
+ existing = fs.readFileSync(globalTypesPath, "utf-8");
472
+ }
473
+ catch {
474
+ existing = null;
475
+ }
476
+ // Skip unnecessary writes (file-watcher loops) and, crucially, don't let the
477
+ // startup write clobber editor-synced theme globals before the editor has
478
+ // connected and re-synced them.
479
+ if (!shouldWriteGlobalTypes(existing, content, cachedThemeSettings != null)) {
480
+ return;
303
481
  }
304
482
  fs.writeFileSync(globalTypesPath, content);
305
483
  }
@@ -323,7 +501,10 @@ function createEditorHandlers(configPath, getConfig, setConfig, wsServer) {
323
501
  const broadcastConfigChange = (componentId) => {
324
502
  const updated = rebuildCompiledComponentFromConfig(getConfig(), componentId);
325
503
  if (updated) {
326
- wsServer.broadcastComponentUpdate(updated);
504
+ wsServer.broadcastComponentUpdate(updated, sharedModulesForComponent(updated));
505
+ if (updated.contentHash) {
506
+ lastBroadcastComponentHashes.set(updated.id, updated.contentHash);
507
+ }
327
508
  }
328
509
  };
329
510
  return {
@@ -701,8 +882,10 @@ function createEditorHandlers(configPath, getConfig, setConfig, wsServer) {
701
882
  regenerateAllComponentTypes(config);
702
883
  console.log(chalk.green(` Deleted enum: ${removed.name} (${enumId})`));
703
884
  },
704
- onSyncTypes: (types, customTypes) => {
885
+ onSyncTypes: (types, customTypes, themeSettings) => {
705
886
  cachedEditorTypes = types;
887
+ if (themeSettings)
888
+ cachedThemeSettings = themeSettings;
706
889
  // Merge incoming custom types with existing ones instead of replacing.
707
890
  // Custom types belong to the code component project and should not be
708
891
  // lost when a different editor project connects.
@@ -808,6 +991,18 @@ async function startDevServer(opts = {}) {
808
991
  // Initial compilation of all components (multi-entry with code splitting)
809
992
  console.log(chalk.cyan(" Compiling components..."));
810
993
  await compileAllComponentsForDev(config);
994
+ // Seed the broadcast-hash maps so the first delta-broadcast after startup doesn't replay
995
+ // the initial state — newly-connecting editors get a full `initial-state` directly via
996
+ // `onRequestState`, independent of the delta path.
997
+ for (const [id, comp] of compiledComponents) {
998
+ if (comp.contentHash)
999
+ lastBroadcastComponentHashes.set(id, comp.contentHash);
1000
+ }
1001
+ for (const [id, mod] of compiledSharedModules) {
1002
+ if (mod.contentHash)
1003
+ lastBroadcastSharedHashes.set(id, mod.contentHash);
1004
+ }
1005
+ lastBroadcastGlobalStyleHash = computeGlobalStyleHash(compiledGlobalStyles);
811
1006
  console.log("");
812
1007
  // Start WebSocket server
813
1008
  const wsServer = new DevServerWebSocket(DEV_SERVER_PORT);
@@ -848,16 +1043,15 @@ async function startDevServer(opts = {}) {
848
1043
  console.log(chalk.yellow(` Recompiling all (triggered by: ${triggeredBy})...`));
849
1044
  const success = await compileAllComponentsForDev(config);
850
1045
  if (success) {
851
- // Send full state to all connected editors
852
- wsServer.broadcast({
853
- type: "initial-state",
854
- payload: {
855
- components: getAllComponents(),
856
- sharedModules: getAllSharedModules(),
857
- globalStyles: compiledGlobalStyles,
858
- timestamp: Date.now(),
859
- },
860
- });
1046
+ const delta = broadcastCompileDelta(wsServer);
1047
+ if (delta.changedComponents === 0 &&
1048
+ delta.deletedComponents === 0 &&
1049
+ !delta.globalStylesChanged) {
1050
+ console.log(chalk.gray(" No editor-visible changes (skipped broadcast)"));
1051
+ }
1052
+ else {
1053
+ console.log(chalk.cyan(` Broadcast delta: ${delta.changedComponents} changed, ${delta.deletedComponents} deleted, ${delta.changedSharedModules} shared chunks changed${delta.globalStylesChanged ? ", globals updated" : ""}`));
1054
+ }
861
1055
  }
862
1056
  else {
863
1057
  // Broadcast error for the triggering component if identifiable
@@ -878,16 +1072,8 @@ async function startDevServer(opts = {}) {
878
1072
  cachedCustomTypes = config.customTypes || [];
879
1073
  writeGlobalTypesFile(config);
880
1074
  await compileAllComponentsForDev(config);
881
- // Send full state to all connected editors
882
- wsServer.broadcast({
883
- type: "initial-state",
884
- payload: {
885
- components: getAllComponents(),
886
- sharedModules: getAllSharedModules(),
887
- globalStyles: compiledGlobalStyles,
888
- timestamp: Date.now(),
889
- },
890
- });
1075
+ const delta = broadcastCompileDelta(wsServer);
1076
+ console.log(chalk.cyan(` Config reload delta: ${delta.changedComponents} changed, ${delta.deletedComponents} deleted, ${delta.changedSharedModules} shared chunks changed${delta.globalStylesChanged ? ", globals updated" : ""}`));
891
1077
  }
892
1078
  catch (e) {
893
1079
  console.error(chalk.red(" Failed to reload config"));