elcrm 1.1.13 → 1.1.15

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 (51) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +1982 -1443
  3. package/package.json +3 -3
  4. package/templates/elcrm-cursor/rules/elcrm-jsdoc.mdc +1 -1
  5. package/templates/elcrm-cursor/rules/elcrm-packages.mdc +1 -1
  6. package/templates/elcrm-cursor/rules/elcrm-ui.mdc +3 -1
  7. package/templates/elcrm-docs/BUTTON.elCRM.md +3 -3
  8. package/templates/elcrm-docs/CLI.elCRM.md +26 -12
  9. package/templates/elcrm-docs/COMPONENTS.elCRM.md +18 -10
  10. package/templates/elcrm-docs/FORM.elCRM.md +11 -3
  11. package/templates/elcrm-docs/ICONS.elCRM.md +2 -0
  12. package/templates/elcrm-docs/NOTIFICATIONS.elCRM.md +1 -1
  13. package/templates/elcrm-docs/SERVER.elCRM.md +3 -1
  14. package/templates/orbit/README.md +12 -0
  15. package/templates/orbit/index.html +28 -0
  16. package/templates/orbit/package.json +39 -0
  17. package/templates/orbit/src/App.tsx +34 -0
  18. package/templates/orbit/src/component/Nav.tsx +139 -0
  19. package/templates/orbit/src/lazyRoute.ts +4 -0
  20. package/templates/orbit/src/lib/icon.tsx +48 -0
  21. package/templates/orbit/src/lib/theme.ts +39 -0
  22. package/templates/orbit/src/main.tsx +54 -0
  23. package/templates/orbit/src/modules/Analytics/index.tsx +5 -0
  24. package/templates/orbit/src/modules/Catalog/index.tsx +7 -0
  25. package/templates/orbit/src/modules/Customers/index.tsx +5 -0
  26. package/templates/orbit/src/modules/Finances/index.tsx +5 -0
  27. package/templates/orbit/src/modules/Home/index.tsx +317 -0
  28. package/templates/orbit/src/modules/Integrations/index.tsx +7 -0
  29. package/templates/orbit/src/modules/Link/index.tsx +7 -0
  30. package/templates/orbit/src/modules/Marketing/index.tsx +5 -0
  31. package/templates/orbit/src/modules/Placeholder.tsx +13 -0
  32. package/templates/orbit/src/modules/Settings/index.tsx +22 -0
  33. package/templates/orbit/src/modules/Store/index.tsx +7 -0
  34. package/templates/orbit/src/pages/Auth.tsx +45 -0
  35. package/templates/orbit/src/pages/Main.tsx +29 -0
  36. package/templates/orbit/src/router-data.d.ts +10 -0
  37. package/templates/orbit/src/state.ts +28 -0
  38. package/templates/orbit/src/style/elcrm.css +27 -0
  39. package/templates/orbit/src/style/theme-dark.css +148 -0
  40. package/templates/orbit/src/style/theme-light.css +148 -0
  41. package/templates/orbit/src/style/theme.css +177 -0
  42. package/templates/orbit/src/vite-env.d.ts +1 -0
  43. package/templates/orbit/tsconfig.json +19 -0
  44. package/templates/orbit/vite.config.ts +14 -0
  45. package/templates/panel/README.md +1 -1
  46. package/templates/panel/web/package.json +9 -9
  47. package/templates/panel/web/src/pages/Auth.tsx +1 -1
  48. package/templates/panel/web/src/style/theme-dark.css +9 -32
  49. package/templates/panel/web/src/style/theme-light.css +9 -32
  50. package/templates/panel/web/src/style/theme.css +20 -39
  51. package/templates/panel/web/vite.config.ts +3 -0
package/dist/index.js CHANGED
@@ -1,22 +1,197 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- var __defProp = Object.defineProperty;
4
- var __returnValue = (v) => v;
5
- function __exportSetter(name, newValue) {
6
- this[name] = __returnValue.bind(null, newValue);
7
- }
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, {
11
- get: all[name],
12
- enumerable: true,
13
- configurable: true,
14
- set: __exportSetter.bind(all, name)
15
- });
16
- };
17
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
3
+
4
+ // src/index.ts
5
+ import { readFileSync as readFileSync23 } from "fs";
6
+ import { dirname as dirname4, join as join18 } from "path";
7
+ import { fileURLToPath as fileURLToPath2 } from "url";
8
+
9
+ // src/parseArgs.ts
10
+ function parseArgs(argv = process.argv.slice(2)) {
11
+ const options = {};
12
+ const flags = [];
13
+ const positionals = [];
14
+ for (let i = 0;i < argv.length; i++) {
15
+ const arg = argv[i];
16
+ if (arg === "--") {
17
+ positionals.push(...argv.slice(i + 1));
18
+ break;
19
+ }
20
+ if (arg.startsWith("--")) {
21
+ const body = arg.slice(2);
22
+ if (body.includes("=")) {
23
+ const eq = body.indexOf("=");
24
+ const key = body.slice(0, eq);
25
+ const value = body.slice(eq + 1);
26
+ if (key)
27
+ options[key] = value;
28
+ continue;
29
+ }
30
+ const next = argv[i + 1];
31
+ if (next && !next.startsWith("-")) {
32
+ const valueOpts = new Set([
33
+ "template",
34
+ "url",
35
+ "name",
36
+ "dir",
37
+ "folder",
38
+ "out"
39
+ ]);
40
+ if (valueOpts.has(body)) {
41
+ options[body] = next;
42
+ i++;
43
+ continue;
44
+ }
45
+ }
46
+ if (body && !flags.includes(body))
47
+ flags.push(body);
48
+ continue;
49
+ }
50
+ if (arg.startsWith("-") && arg.length > 1) {
51
+ const short = arg.slice(1);
52
+ if (short === "t" && argv[i + 1] && !argv[i + 1].startsWith("-")) {
53
+ options.template = argv[i + 1];
54
+ i++;
55
+ continue;
56
+ }
57
+ if (short && !flags.includes(short))
58
+ flags.push(short);
59
+ continue;
60
+ }
61
+ positionals.push(arg);
62
+ }
63
+ const [command = "", ...rest] = positionals;
64
+ return {
65
+ command,
66
+ positionals: rest,
67
+ options,
68
+ flags
69
+ };
70
+ }
71
+ function hasFlag(parsed, ...names) {
72
+ return names.some((n) => parsed.flags.includes(n));
73
+ }
74
+
75
+ // src/lib/test/detect.ts
76
+ import { existsSync, readdirSync, readFileSync, statSync } from "fs";
77
+ import { join } from "path";
78
+ function hasElcrmServer(pkg) {
79
+ for (const section of [
80
+ "dependencies",
81
+ "devDependencies",
82
+ "peerDependencies"
83
+ ]) {
84
+ const map = pkg[section];
85
+ if (map && typeof map === "object" && "@elcrm/server" in map)
86
+ return true;
87
+ }
88
+ return false;
89
+ }
90
+ function detectTestMode(pkg, force) {
91
+ if (force === "server" || force === "front")
92
+ return force;
93
+ return hasElcrmServer(pkg) ? "server" : "front";
94
+ }
95
+ function listWorkspaceDirs(cwd, pkg) {
96
+ const raw = pkg.workspaces;
97
+ let patterns = [];
98
+ if (Array.isArray(raw)) {
99
+ patterns = raw.filter((p) => typeof p === "string");
100
+ } else if (raw && typeof raw === "object" && Array.isArray(raw.packages)) {
101
+ patterns = raw.packages.filter((p) => typeof p === "string");
102
+ }
103
+ if (!patterns.length)
104
+ return [];
105
+ const out = [];
106
+ for (const pat of patterns) {
107
+ if (!pat.includes("*")) {
108
+ const dir = join(cwd, pat);
109
+ if (existsSync(join(dir, "package.json")))
110
+ out.push(dir);
111
+ continue;
112
+ }
113
+ const base = pat.replace(/\/\*$/, "").replace(/\*$/, "");
114
+ const parent = join(cwd, base);
115
+ if (!existsSync(parent) || !statSync(parent).isDirectory())
116
+ continue;
117
+ for (const name of readdirSync(parent)) {
118
+ const dir = join(parent, name);
119
+ if (statSync(dir).isDirectory() && existsSync(join(dir, "package.json"))) {
120
+ out.push(dir);
121
+ }
122
+ }
123
+ }
124
+ return out;
125
+ }
126
+ function readPkgAt(dir) {
127
+ const path = join(dir, "package.json");
128
+ if (!existsSync(path))
129
+ return null;
130
+ try {
131
+ return JSON.parse(readFileSync(path, "utf8"));
132
+ } catch {
133
+ return null;
134
+ }
135
+ }
136
+
137
+ // src/lib/pkg.ts
138
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
139
+ import { join as join2 } from "path";
140
+ function packageJsonPath(cwd = process.cwd()) {
141
+ return join2(cwd, "package.json");
142
+ }
143
+ function readPackageJson(cwd = process.cwd()) {
144
+ const path = packageJsonPath(cwd);
145
+ if (!existsSync2(path))
146
+ return null;
147
+ try {
148
+ return JSON.parse(readFileSync2(path, "utf8"));
149
+ } catch {
150
+ return null;
151
+ }
152
+ }
153
+ function writePackageJson(pkg, cwd = process.cwd()) {
154
+ writeFileSync(packageJsonPath(cwd), JSON.stringify(pkg, null, 2) + `
155
+ `, "utf8");
156
+ }
157
+
158
+ // src/lib/elcrmDeps.ts
159
+ var SECTIONS = [
160
+ "dependencies",
161
+ "devDependencies",
162
+ "peerDependencies"
163
+ ];
164
+ function listElcrmDeps(pkg) {
165
+ const out = [];
166
+ for (const section of SECTIONS) {
167
+ const map = pkg[section];
168
+ if (!map || typeof map !== "object")
169
+ continue;
170
+ for (const [name, version] of Object.entries(map)) {
171
+ if (name.startsWith("@elcrm/") && typeof version === "string") {
172
+ out.push({ name, version, section });
173
+ }
174
+ }
175
+ }
176
+ return out;
177
+ }
178
+ function uniqueElcrmNames(deps) {
179
+ return [...new Set(deps.map((d) => d.name))].sort();
180
+ }
181
+ function uniqueElcrmNamesInTree(cwd, pkg) {
182
+ const set = new Set(uniqueElcrmNames(listElcrmDeps(pkg)));
183
+ for (const dir of listWorkspaceDirs(cwd, pkg)) {
184
+ const child = readPackageJson(dir);
185
+ if (!child)
186
+ continue;
187
+ for (const name of uniqueElcrmNames(listElcrmDeps(child)))
188
+ set.add(name);
189
+ }
190
+ return [...set].sort();
191
+ }
18
192
 
19
193
  // src/lib/latest.ts
194
+ var CACHE = new Map;
20
195
  async function fetchLatestVersion(name) {
21
196
  if (CACHE.has(name))
22
197
  return CACHE.get(name);
@@ -68,184 +243,96 @@ async function fetchCorePinnedVersions() {
68
243
  } catch {}
69
244
  return map;
70
245
  }
71
- var CACHE;
72
- var init_latest = __esm(() => {
73
- CACHE = new Map;
74
- });
75
246
 
76
- // src/lib/pkg.ts
77
- import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
78
- import { join as join2 } from "path";
79
- function packageJsonPath(cwd = process.cwd()) {
80
- return join2(cwd, "package.json");
81
- }
82
- function readPackageJson(cwd = process.cwd()) {
83
- const path = packageJsonPath(cwd);
84
- if (!existsSync2(path))
85
- return null;
86
- try {
87
- return JSON.parse(readFileSync(path, "utf8"));
88
- } catch {
89
- return null;
247
+ // src/lib/install.ts
248
+ import { existsSync as existsSync3 } from "fs";
249
+ import { join as join3 } from "path";
250
+ import { spawnSync } from "child_process";
251
+ function detectPackageManager(cwd = process.cwd()) {
252
+ if (existsSync3(join3(cwd, "bun.lockb")) || existsSync3(join3(cwd, "bun.lock"))) {
253
+ return "bun";
90
254
  }
255
+ const bun = spawnSync("bun", ["--version"], { encoding: "utf8" });
256
+ if (bun.status === 0)
257
+ return "bun";
258
+ return "npm";
91
259
  }
92
- function writePackageJson(pkg, cwd = process.cwd()) {
93
- writeFileSync(packageJsonPath(cwd), JSON.stringify(pkg, null, 2) + `
94
- `, "utf8");
260
+ function installDeps(cwd = process.cwd()) {
261
+ const pm = detectPackageManager(cwd);
262
+ console.log(`\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439 (${pm} install)\u2026`);
263
+ const r = spawnSync(pm, ["install"], {
264
+ cwd,
265
+ stdio: "inherit",
266
+ env: process.env
267
+ });
268
+ return r.status === 0;
269
+ }
270
+ function installGlobalElcrm() {
271
+ const bun = spawnSync("bun", ["--version"], { encoding: "utf8" });
272
+ const cmd = bun.status === 0 ? "bun" : "npm";
273
+ const args = ["i", "-g", "elcrm@latest"];
274
+ console.log(`CLI: ${cmd} ${args.join(" ")}`);
275
+ const r = spawnSync(cmd, args, {
276
+ stdio: "inherit",
277
+ env: process.env
278
+ });
279
+ return r.status === 0;
95
280
  }
96
- var init_pkg = () => {};
97
281
 
98
- // src/lib/migrate/socket-server.ts
282
+ // src/lib/ansi.ts
283
+ var enabled = process.env.NO_COLOR == null && process.env.FORCE_COLOR !== "0" && Boolean(process.stdout.isTTY);
284
+ function wrap(code, text) {
285
+ if (!enabled)
286
+ return text;
287
+ return `\x1B[${code}m${text}\x1B[0m`;
288
+ }
289
+ var c = {
290
+ gray: (t) => wrap("90", t),
291
+ green: (t) => wrap("32", t),
292
+ blue: (t) => wrap("34", t),
293
+ yellow: (t) => wrap("33", t),
294
+ red: (t) => wrap("31", t),
295
+ cyan: (t) => wrap("36", t),
296
+ dim: (t) => wrap("2", t)
297
+ };
298
+
299
+ // src/lib/cssSync.ts
300
+ import {
301
+ existsSync as existsSync5,
302
+ readdirSync as readdirSync3,
303
+ readFileSync as readFileSync7,
304
+ statSync as statSync3,
305
+ writeFileSync as writeFileSync3
306
+ } from "fs";
307
+ import { createRequire } from "module";
308
+ import { dirname, join as join5 } from "path";
309
+
310
+ // src/lib/migrate/form-tokens.ts
311
+ import { readFileSync as readFileSync5 } from "fs";
312
+
313
+ // src/lib/migrate/walk.ts
99
314
  import {
100
- existsSync as existsSync3,
101
- readdirSync,
102
- readFileSync as readFileSync2,
103
- statSync,
315
+ existsSync as existsSync4,
316
+ readdirSync as readdirSync2,
317
+ readFileSync as readFileSync3,
318
+ statSync as statSync2,
104
319
  writeFileSync as writeFileSync2
105
320
  } from "fs";
106
- import { join as join3 } from "path";
107
- function walkFiles(dir, out = []) {
108
- if (!existsSync3(dir))
109
- return out;
110
- for (const name of readdirSync(dir)) {
111
- if (SKIP_DIRS.has(name))
112
- continue;
113
- const full = join3(dir, name);
114
- const st = statSync(full);
115
- if (st.isDirectory())
116
- walkFiles(full, out);
117
- else {
118
- const ext = name.includes(".") ? `.${name.split(".").pop()}` : "";
119
- if (TEXT_EXT.has(ext))
120
- out.push(full);
121
- }
122
- }
123
- return out;
124
- }
125
- function rewriteImports(source) {
126
- let hits = 0;
127
- let next = source;
128
- const patterns = [
129
- [
130
- /from\s+["']@elcrm\/socket\/server["']/g,
131
- 'from "@elcrm/server"'
132
- ],
133
- [
134
- /from\s+["']@elcrm\/socket-server["']/g,
135
- 'from "@elcrm/server"'
136
- ],
137
- [
138
- /require\s*\(\s*["']@elcrm\/socket\/server["']\s*\)/g,
139
- 'require("@elcrm/server")'
140
- ],
141
- [
142
- /require\s*\(\s*["']@elcrm\/socket-server["']\s*\)/g,
143
- 'require("@elcrm/server")'
144
- ]
145
- ];
146
- for (const [re, repl] of patterns) {
147
- const before = next;
148
- next = next.replace(re, () => {
149
- hits++;
150
- return repl;
151
- });
152
- }
153
- return { next, hits };
154
- }
155
- async function migrateSocketServer(cwd, dry) {
156
- const changes = [];
157
- const pkg = readPackageJson(cwd);
158
- let removedSocketServer = false;
159
- if (pkg) {
160
- for (const section of [
161
- "dependencies",
162
- "devDependencies",
163
- "peerDependencies"
164
- ]) {
165
- const map = pkg[section];
166
- if (!map)
167
- continue;
168
- if (map["@elcrm/socket-server"]) {
169
- removedSocketServer = true;
170
- changes.push({
171
- file: "package.json",
172
- detail: `\u0443\u0434\u0430\u043B\u0438\u0442\u044C ${section}["@elcrm/socket-server"]`
173
- });
174
- if (!dry)
175
- delete map["@elcrm/socket-server"];
176
- }
177
- }
178
- const hasServer = Boolean(pkg.dependencies?.["@elcrm/server"]) || Boolean(pkg.devDependencies?.["@elcrm/server"]);
179
- if (removedSocketServer && !hasServer) {
180
- const latest = dry ? null : await fetchLatestVersion("@elcrm/server");
181
- const ver = latest ? `^${latest}` : "^0.5.3";
182
- changes.push({
183
- file: "package.json",
184
- detail: `\u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C dependencies["@elcrm/server"] = ${ver}`
185
- });
186
- if (!dry) {
187
- if (!pkg.dependencies)
188
- pkg.dependencies = {};
189
- pkg.dependencies["@elcrm/server"] = ver;
190
- }
191
- }
192
- if (!dry && changes.some((c2) => c2.file === "package.json")) {
193
- writePackageJson(pkg, cwd);
194
- }
195
- }
196
- const roots = ["src", "app", "server", "lib"].map((d) => join3(cwd, d));
197
- const files = roots.flatMap((r) => walkFiles(r));
198
- for (const file of files) {
199
- const raw = readFileSync2(file, "utf8");
200
- const { next, hits } = rewriteImports(raw);
201
- if (hits > 0) {
202
- changes.push({
203
- file: file.replace(cwd + "/", ""),
204
- detail: `${hits} \u0437\u0430\u043C\u0435\u043D(\u044B) \u0438\u043C\u043F\u043E\u0440\u0442\u043E\u0432 \u2192 @elcrm/server`
205
- });
206
- if (!dry)
207
- writeFileSync2(file, next, "utf8");
208
- }
209
- }
210
- return changes;
211
- }
212
- var SKIP_DIRS, TEXT_EXT;
213
- var init_socket_server = __esm(() => {
214
- init_latest();
215
- init_pkg();
216
- SKIP_DIRS = new Set([
217
- "node_modules",
218
- "dist",
219
- ".git",
220
- "coverage",
221
- ".turbo"
222
- ]);
223
- TEXT_EXT = new Set([
224
- ".ts",
225
- ".tsx",
226
- ".js",
227
- ".jsx",
228
- ".mjs",
229
- ".cjs",
230
- ".mts",
231
- ".cts"
232
- ]);
233
- });
234
-
235
- // src/lib/migrate/walk.ts
236
- import {
237
- existsSync as existsSync4,
238
- readdirSync as readdirSync2,
239
- readFileSync as readFileSync3,
240
- statSync as statSync2,
241
- writeFileSync as writeFileSync3
242
- } from "fs";
243
321
  import { join as join4, relative } from "path";
322
+ var SKIP_DIRS = new Set([
323
+ "node_modules",
324
+ "dist",
325
+ ".git",
326
+ "coverage",
327
+ ".turbo",
328
+ ".next",
329
+ "build"
330
+ ]);
244
331
  function walkCodeFiles(dir, out = []) {
245
332
  if (!existsSync4(dir))
246
333
  return out;
247
334
  for (const name of readdirSync2(dir)) {
248
- if (SKIP_DIRS2.has(name))
335
+ if (SKIP_DIRS.has(name))
249
336
  continue;
250
337
  const full = join4(dir, name);
251
338
  let st;
@@ -272,373 +359,197 @@ function writeIfChanged(file, next, dry) {
272
359
  if (prev === next)
273
360
  return false;
274
361
  if (!dry)
275
- writeFileSync3(file, next, "utf8");
362
+ writeFileSync2(file, next, "utf8");
276
363
  return true;
277
364
  }
278
- var SKIP_DIRS2;
279
- var init_walk = __esm(() => {
280
- SKIP_DIRS2 = new Set([
281
- "node_modules",
282
- "dist",
283
- ".git",
284
- "coverage",
285
- ".turbo",
286
- ".next",
287
- "build"
288
- ]);
289
- });
290
365
 
291
- // src/lib/migrate/size-sml.ts
292
- import { readFileSync as readFileSync4 } from "fs";
293
- function rewrite(source) {
294
- let next = source;
295
- let hits = 0;
296
- for (const [re, repl] of PATTERNS) {
297
- next = next.replace(re, () => {
298
- hits++;
299
- return repl;
300
- });
301
- }
302
- return { next, hits };
303
- }
304
- async function migrateSizeSml(cwd, dry) {
305
- const changes = [];
306
- const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r));
307
- for (const file of files) {
308
- if (file.endsWith(".css"))
309
- continue;
310
- const raw = readFileSync4(file, "utf8");
311
- const { next, hits } = rewrite(raw);
312
- if (hits === 0)
313
- continue;
314
- if (writeIfChanged(file, next, dry) || dry) {
315
- changes.push({
316
- file: rel(cwd, file),
317
- detail: `${hits}\xD7 size sm/md \u2192 s/m`
318
- });
366
+ // src/lib/migrate/css-decl.ts
367
+ function removeCssDecl(css, name) {
368
+ const re = new RegExp(`\\n?[ \\t]*${name.replace(/-/g, "\\-")}(?![\\w-])\\s*:`, "g");
369
+ let n = 0;
370
+ const ranges = [];
371
+ let m;
372
+ while ((m = re.exec(css)) !== null) {
373
+ const start = m.index;
374
+ let i = m.index + m[0].length;
375
+ let depth = 0;
376
+ while (i < css.length) {
377
+ const ch = css[i];
378
+ if (ch === "(")
379
+ depth++;
380
+ else if (ch === ")")
381
+ depth = Math.max(0, depth - 1);
382
+ else if ((ch === ";" || ch === "}") && depth === 0) {
383
+ if (ch === ";")
384
+ i++;
385
+ break;
386
+ }
387
+ i++;
319
388
  }
389
+ let s = start;
390
+ if (s > 0 && css[s - 1] === `
391
+ `)
392
+ s--;
393
+ ranges.push({ start: s, end: i });
394
+ n++;
320
395
  }
321
- return changes;
396
+ if (!ranges.length)
397
+ return { next: css, n: 0 };
398
+ let next = css;
399
+ for (let r = ranges.length - 1;r >= 0; r--) {
400
+ const { start, end } = ranges[r];
401
+ next = next.slice(0, start) + next.slice(end);
402
+ }
403
+ return { next: next.replace(/\n{3,}/g, `
404
+
405
+ `), n };
322
406
  }
323
- var PATTERNS;
324
- var init_size_sml = __esm(() => {
325
- init_walk();
326
- PATTERNS = [
327
- [/size\s*=\s*"sm"/g, 'size="s"'],
328
- [/size\s*=\s*'sm'/g, "size='s'"],
329
- [/size\s*=\s*\{\s*"sm"\s*\}/g, 'size={"s"}'],
330
- [/size\s*=\s*\{\s*'sm'\s*\}/g, "size={'s'}"],
331
- [/size\s*=\s*"md"/g, 'size="m"'],
332
- [/size\s*=\s*'md'/g, "size='m'"],
333
- [/size\s*=\s*\{\s*"md"\s*\}/g, 'size={"m"}'],
334
- [/size\s*=\s*\{\s*'md'\s*\}/g, "size={'m'}"]
335
- ];
336
- });
337
407
 
338
- // src/lib/migrate/field-border.ts
339
- import { readFileSync as readFileSync5 } from "fs";
340
- function extractColor(value) {
341
- const v = value.trim().replace(/;$/, "");
342
- const m = v.match(/^(?:\d+(?:\.\d+)?(?:px|em|rem)?\s+)?(?:solid|dashed|dotted|double)\s+(.+)$/i);
343
- if (m)
344
- return m[1].trim();
345
- if (/^(#|rgb|hsl|var\(|color-mix|transparent|currentColor)/i.test(v)) {
346
- return null;
408
+ // src/lib/migrate/css-sanitize.ts
409
+ import { readFileSync as readFileSync4 } from "fs";
410
+ var MIX_SPACE_RE = /(?:in\s+srgb|in\s+hsl|in\s+hwb|in\s+lab|in\s+lch)/i;
411
+ var ORPHAN_MIX_BLOCK_RE = /(?:^|\n)([ \t]*(?:in\s+srgb|in\s+hsl|in\s+hwb|in\s+lab|in\s+lch)[\s\S]*?\n[ \t]*\);?[ \t]*)(?=\n|$)/gi;
412
+ function prevNonEmptyLine(source, index) {
413
+ const before = source.slice(0, index);
414
+ const lines = before.split(/\r?\n/);
415
+ for (let i = lines.length - 1;i >= 0; i--) {
416
+ const t = (lines[i] ?? "").trimEnd();
417
+ if (t.length > 0)
418
+ return t;
347
419
  }
348
- return null;
420
+ return "";
349
421
  }
350
- function rewriteCss(source) {
422
+ function isInsideColorMix(prevLine) {
423
+ return /color-mix\s*\(\s*$/i.test(prevLine);
424
+ }
425
+ function stripOrphanColorMix(source) {
351
426
  let hits = 0;
352
- let next = source;
353
- for (const name of VARS) {
354
- const re = new RegExp(`(${name.replace(/-/g, "\\-")}\\s*:\\s*)([^;\\n}]+)`, "g");
355
- next = next.replace(re, (full, prefix, rawVal) => {
356
- const color = extractColor(rawVal);
357
- if (!color)
358
- return full;
427
+ let next = source.replace(ORPHAN_MIX_BLOCK_RE, (match, _block, offset) => {
428
+ const contentStart = offset + (match.startsWith(`
429
+ `) ? 1 : 0);
430
+ const prev = prevNonEmptyLine(source, contentStart);
431
+ if (isInsideColorMix(prev)) {
432
+ return match;
433
+ }
434
+ hits++;
435
+ return match.startsWith(`
436
+ `) ? `
437
+ ` : "";
438
+ });
439
+ const lines = next.split(/\r?\n/);
440
+ const kept = [];
441
+ let mixOpen = 0;
442
+ for (const line of lines) {
443
+ const opens = (line.match(/color-mix\s*\(/gi) ?? []).length;
444
+ if (opens)
445
+ mixOpen += opens;
446
+ const trimmed = line.trim();
447
+ const orphanAlone = mixOpen === 0 && (/^in\s+(?:srgb|hsl|hwb|lab|lch)\s*,?\s*$/i.test(trimmed) || /^transparent\s*\)\s*;?\s*$/i.test(trimmed));
448
+ if (orphanAlone) {
359
449
  hits++;
360
- return `${prefix}${color}`;
361
- });
450
+ continue;
451
+ }
452
+ kept.push(line);
453
+ if (mixOpen > 0) {
454
+ if (/\)\s*;?\s*$/.test(trimmed) && !/^var\(/.test(trimmed)) {
455
+ const closes = (trimmed.match(/\)/g) ?? []).length;
456
+ const vars = (trimmed.match(/var\s*\(/g) ?? []).length;
457
+ mixOpen = Math.max(0, mixOpen - Math.max(0, closes - vars));
458
+ }
459
+ }
362
460
  }
461
+ next = kept.join(`
462
+ `);
463
+ next = next.replace(/[ \t]*[-\w]+\s*:\s*color-mix\(\s*\n(?:[ \t]*\n)+(?=[ \t]*(?:[-\w]+\s*:|\}))/g, () => {
464
+ hits++;
465
+ return "";
466
+ });
467
+ next = next.replace(/[ \t]*[-\w]+\s*:\s*color-mix\(\s*\n[ \t]*\}/g, () => {
468
+ hits++;
469
+ return "}";
470
+ });
471
+ next = next.replace(/\n{3,}/g, `
472
+
473
+ `);
363
474
  return { next, hits };
364
475
  }
365
- async function migrateFieldBorder(cwd, dry) {
476
+ function lineHasOrphanColorMix(line, prevNonEmpty) {
477
+ const t = line.trim();
478
+ if (!t || t.startsWith("/*") || t.startsWith("*"))
479
+ return false;
480
+ if (MIX_SPACE_RE.test(t) && /color-mix\s*\(/i.test(t))
481
+ return false;
482
+ if (/^in\s+(?:srgb|hsl|hwb|lab|lch)\b/i.test(t) && !/color-mix\s*\(/i.test(t)) {
483
+ if (prevNonEmpty != null && isInsideColorMix(prevNonEmpty))
484
+ return false;
485
+ return true;
486
+ }
487
+ if (/^transparent\s*\)\s*;?\s*$/i.test(t)) {
488
+ if (prevNonEmpty != null && /color-mix\s*\(/i.test(prevNonEmpty)) {
489
+ return false;
490
+ }
491
+ if (prevNonEmpty != null && MIX_SPACE_RE.test(prevNonEmpty))
492
+ return false;
493
+ if (prevNonEmpty != null && /%\s*,?\s*$/.test(prevNonEmpty.trim())) {
494
+ return false;
495
+ }
496
+ return true;
497
+ }
498
+ return false;
499
+ }
500
+ async function migrateCssSanitize(cwd, dry) {
366
501
  const changes = [];
367
502
  const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r)).filter((f) => f.endsWith(".css"));
368
503
  for (const file of files) {
369
- const raw = readFileSync5(file, "utf8");
370
- const { next, hits } = rewriteCss(raw);
371
- if (hits === 0)
504
+ const raw = readFileSync4(file, "utf8");
505
+ const { next, hits } = stripOrphanColorMix(raw);
506
+ if (hits === 0 || next === raw)
372
507
  continue;
373
508
  if (writeIfChanged(file, next, dry) || dry) {
374
509
  changes.push({
375
510
  file: rel(cwd, file),
376
- detail: `${hits}\xD7 border hover/focus \u2192 \u0442\u043E\u043B\u044C\u043A\u043E \u0446\u0432\u0435\u0442`
511
+ detail: `${hits}\xD7 \u0443\u0434\u0430\u043B\u0435\u043D\u044B \u043E\u0441\u0438\u0440\u043E\u0442\u0435\u0432\u0448\u0438\u0435 color-mix/\u0444\u0440\u0430\u0433\u043C\u0435\u043D\u0442\u044B`
377
512
  });
378
513
  }
379
514
  }
380
515
  return changes;
381
516
  }
382
- var VARS;
383
- var init_field_border = __esm(() => {
384
- init_walk();
385
- VARS = [
386
- "--field-border-focus",
387
- "--field-border-hover",
388
- "--field-border-error",
389
- "--field-border-disabled"
390
- ];
391
- });
392
-
393
- // src/lib/migrate/modal-create.ts
394
- import { readFileSync as readFileSync6 } from "fs";
395
- async function migrateModalCreate(cwd, dry) {
396
- const changes = [];
397
- const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r)).filter((f) => /\.(tsx|jsx)$/.test(f));
398
- const addRe = /Modal\.Add\s*\(\s*<([A-Za-z_][\w.]*)\s+key=\{Date\.now\(\)\}/;
399
- for (const file of files) {
400
- const raw = readFileSync6(file, "utf8");
401
- if (!addRe.test(raw) && !/Modal\.Add\s*\(/.test(raw))
402
- continue;
403
- if (/export\s+default\s+Modal\.Create\s*\(/.test(raw)) {
404
- if (/Modal\.Add\s*\(/.test(raw)) {
405
- changes.push({
406
- file: rel(cwd, file),
407
- detail: "\u043E\u0441\u0442\u0430\u043B\u0441\u044F Modal.Add \u0440\u044F\u0434\u043E\u043C \u0441 Create \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 Add \u0432\u0440\u0443\u0447\u043D\u0443\u044E"
408
- });
409
- }
410
- continue;
411
- }
412
- const simple = /function\s+Screen\b/.test(raw) || /const\s+Screen\s*=/.test(raw) || /function\s+\w+\s*\([^)]*\)\s*\{[\s\S]*Modal\.Main/.test(raw);
413
- const openWrapper = /(?:const|function)\s+Open\s*=\s*(?:\([^)]*\)|[A-Za-z_]\w*)\s*=>\s*\{?\s*Modal\.Add/;
414
- if (simple && openWrapper.test(raw) && /export\s+default/.test(raw)) {
415
- let next = raw;
416
- next = next.replace(/\n(?:const|function)\s+Open\s*=\s*[\s\S]*?(?=\n(?:const|function|export)\s)/, `
417
- `);
418
- next = next.replace(/\n(?:const|function)\s+Icon\s*=\s*[\s\S]*?(?=\n(?:const|function|export)\s)/, `
419
- `);
420
- next = next.replace(/\nconst\s+Add\s*=\s*\{[\s\S]*?\};\s*\nexport\s+default\s+Add\s*;?/, `
421
- export default Modal.Create(Screen);
422
- `);
423
- next = next.replace(/export\s+default\s+\{\s*Open\s*,?\s*Icon?\s*,?\s*\}\s*;?/, "export default Modal.Create(Screen);");
424
- next = next.replace(/export\s+function\s+open\w+\s*\([^)]*\)\s*\{[\s\S]*?Modal\.Add\([\s\S]*?\);\s*\}/, (block) => {
425
- return `/* TODO: \u0437\u0430\u043C\u0435\u043D\u0438\u0442\u0435 \u0432\u044B\u0437\u043E\u0432\u044B \u043D\u0430 Modal.Open("Module","Name", props) \u0438\u043B\u0438 Create.Open */
426
- ${block}`;
427
- });
428
- if (next !== raw && !/Modal\.Add/.test(next)) {
429
- if (writeIfChanged(file, next, dry) || dry) {
430
- changes.push({
431
- file: rel(cwd, file),
432
- detail: "Modal.Add \u2192 Modal.Create(Screen)"
433
- });
434
- }
435
- continue;
436
- }
437
- }
438
- const count = (raw.match(/Modal\.Add\s*\(/g) || []).length;
439
- changes.push({
440
- file: rel(cwd, file),
441
- detail: `${count}\xD7 Modal.Add \u2014 \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0438\u0442\u0435 \u043D\u0430 Modal.Create + Open (\u0441\u043C. Projects/modal/Create)`
442
- });
443
- }
444
- return changes;
445
- }
446
- var init_modal_create = __esm(() => {
447
- init_walk();
448
- });
449
-
450
- // src/lib/migrate/legacy-hacks.ts
451
- import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
452
- import { join as join5 } from "path";
453
- async function migrateLegacyReactHack(cwd, dry) {
454
- const changes = [];
455
- const files = new Set(projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r)).filter((f) => /(^|\/)(main|bootstrap)\.(tsx?|jsx?)$/.test(f)));
456
- for (const name of ["main.tsx", "main.ts", "src/main.tsx", "src/main.ts"]) {
457
- const full = join5(cwd, name);
458
- if (existsSync5(full))
459
- files.add(full);
460
- }
461
- for (const file of files) {
462
- const raw = readFileSync7(file, "utf8");
463
- if (!HACK_RE.test(raw))
464
- continue;
465
- const next = raw.replace(/\/\*\s*@elcrm\/modal[\s\S]*?\*\/\s*/m, "").replace(/\(globalThis\s+as\s+typeof\s+globalThis\s+&\s+\{\s*React:\s*typeof\s+React\s*\}\)\.React\s*=\s*React\s*;\s*/g, "").replace(/globalThis\.React\s*=\s*React\s*;\s*/g, "");
466
- if (next === raw) {
467
- changes.push({
468
- file: rel(cwd, file),
469
- detail: "globalThis.React \u2014 \u0443\u0434\u0430\u043B\u0438\u0442\u0435 \u0432\u0440\u0443\u0447\u043D\u0443\u044E \u043F\u043E\u0441\u043B\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F @elcrm/modal"
470
- });
471
- continue;
472
- }
473
- if (writeIfChanged(file, next, dry) || dry) {
474
- changes.push({
475
- file: rel(cwd, file),
476
- detail: "\u0443\u0434\u0430\u043B\u0451\u043D globalThis.React \u0445\u0430\u043A"
477
- });
478
- }
479
- }
480
- return changes;
481
- }
482
- var HACK_RE;
483
- var init_legacy_hacks = __esm(() => {
484
- init_walk();
485
- HACK_RE = /\(globalThis\s+as\s+[^)]+\)\.React\s*=\s*React\s*;?|globalThis\.React\s*=\s*React\s*;?/;
486
- });
487
-
488
- // src/lib/migrate/form-aliases.ts
489
- import { readFileSync as readFileSync8 } from "fs";
490
- function rewrite2(source) {
491
- let next = source;
492
- const labels = [];
493
- for (const [re, repl, label] of REPLACEMENTS) {
494
- let n = 0;
495
- next = next.replace(re, () => {
496
- n++;
497
- return repl;
498
- });
499
- if (n > 0)
500
- labels.push(`${n}\xD7 ${label}`);
501
- }
502
- return { next, labels };
503
- }
504
- async function migrateFormAliases(cwd, dry) {
505
- const changes = [];
506
- const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r));
507
- for (const file of files) {
508
- if (file.endsWith(".css"))
509
- continue;
510
- const raw = readFileSync8(file, "utf8");
511
- const { next, labels } = rewrite2(raw);
512
- if (labels.length === 0)
513
- continue;
514
- if (writeIfChanged(file, next, dry) || dry) {
515
- changes.push({
516
- file: rel(cwd, file),
517
- detail: labels.join(", ")
518
- });
519
- }
520
- }
521
- return changes;
522
- }
523
- var REPLACEMENTS;
524
- var init_form_aliases = __esm(() => {
525
- init_walk();
526
- REPLACEMENTS = [
527
- [/\bProgressField\b/g, "RangeField", "ProgressField\u2192RangeField"],
528
- [/\bReadonlyField\b/g, "DisplayField", "ReadonlyField\u2192DisplayField"],
529
- [/\bOtpField\b/g, "CodeField", "OtpField\u2192CodeField"],
530
- [/\bSegmentField\b/g, "TabsField", "SegmentField\u2192TabsField"],
531
- [/\bDragonDropField\b/g, "DragDropField", "DragonDropField\u2192DragDropField"],
532
- [/\bTProgress\b/g, "TRange", "TProgress\u2192TRange"]
533
- ];
534
- });
535
-
536
- // src/lib/migrate/css-decl.ts
537
- function removeCssDecl(css, name) {
538
- const re = new RegExp(`\\n?[ \\t]*${name.replace(/-/g, "\\-")}(?![\\w-])\\s*:`, "g");
539
- let n = 0;
540
- const ranges = [];
541
- let m;
542
- while ((m = re.exec(css)) !== null) {
543
- const start = m.index;
544
- let i = m.index + m[0].length;
545
- let depth = 0;
546
- while (i < css.length) {
547
- const ch = css[i];
548
- if (ch === "(")
549
- depth++;
550
- else if (ch === ")")
551
- depth = Math.max(0, depth - 1);
552
- else if ((ch === ";" || ch === "}") && depth === 0) {
553
- if (ch === ";")
554
- i++;
555
- break;
556
- }
557
- i++;
558
- }
559
- let s = start;
560
- if (s > 0 && css[s - 1] === `
561
- `)
562
- s--;
563
- ranges.push({ start: s, end: i });
564
- n++;
565
- }
566
- if (!ranges.length)
567
- return { next: css, n: 0 };
568
- let next = css;
569
- for (let r = ranges.length - 1;r >= 0; r--) {
570
- const { start, end } = ranges[r];
571
- next = next.slice(0, start) + next.slice(end);
572
- }
573
- return { next: next.replace(/\n{3,}/g, `
574
-
575
- `), n };
576
- }
577
-
578
- // src/lib/migrate/css-sanitize.ts
579
- import { readFileSync as readFileSync9 } from "fs";
580
- function stripOrphanColorMix(source) {
581
- let next = source;
582
- let hits = 0;
583
- next = next.replace(ORPHAN_MIX_BLOCK_RE, () => {
584
- hits++;
585
- return `
586
- `;
587
- });
588
- const lines = next.split(/\r?\n/);
589
- const kept = [];
590
- for (const line of lines) {
591
- if (/^[ \t]*in\s+(?:srgb|hsl|hwb|lab|lch)\s*,?\s*$/i.test(line) || /^[ \t]*transparent\s*\)\s*;?\s*$/i.test(line) || /^[ \t]*var\([^)]+\)\s*\d+%\s*,?\s*$/i.test(line)) {
592
- const prev = kept[kept.length - 1] ?? "";
593
- if (!/color-mix\s*\(\s*$/i.test(prev) && !/color-mix\s*\(/i.test(prev)) {
594
- hits++;
595
- continue;
596
- }
597
- }
598
- kept.push(line);
599
- }
600
- next = kept.join(`
601
- `).replace(/\n{3,}/g, `
602
-
603
- `);
604
- return { next, hits };
605
- }
606
- function lineHasOrphanColorMix(line) {
607
- const t = line.trim();
608
- if (!t || t.startsWith("/*") || t.startsWith("*"))
609
- return false;
610
- if (/^in\s+(?:srgb|hsl|hwb|lab|lch)\b/i.test(t) && !/color-mix\s*\(/i.test(t)) {
611
- return true;
612
- }
613
- if (/^transparent\s*\)\s*;?\s*$/i.test(t))
614
- return true;
615
- return false;
616
- }
617
- async function migrateCssSanitize(cwd, dry) {
618
- const changes = [];
619
- const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r)).filter((f) => f.endsWith(".css"));
620
- for (const file of files) {
621
- const raw = readFileSync9(file, "utf8");
622
- const { next, hits } = stripOrphanColorMix(raw);
623
- if (hits === 0 || next === raw)
624
- continue;
625
- if (writeIfChanged(file, next, dry) || dry) {
626
- changes.push({
627
- file: rel(cwd, file),
628
- detail: `${hits}\xD7 \u0443\u0434\u0430\u043B\u0435\u043D\u044B \u043E\u0441\u0438\u0440\u043E\u0442\u0435\u0432\u0448\u0438\u0435 color-mix/\u0444\u0440\u0430\u0433\u043C\u0435\u043D\u0442\u044B`
629
- });
630
- }
631
- }
632
- return changes;
633
- }
634
- var ORPHAN_MIX_BLOCK_RE;
635
- var init_css_sanitize = __esm(() => {
636
- init_walk();
637
- ORPHAN_MIX_BLOCK_RE = /(?:^|\n)[ \t]*(?:in\s+srgb|in\s+hsl|in\s+hwb|in\s+lab|in\s+lch)[\s\S]*?\n[ \t]*\);?[ \t]*(?=\n|$)/gi;
638
- });
639
517
 
640
518
  // src/lib/migrate/form-tokens.ts
641
- import { readFileSync as readFileSync10 } from "fs";
519
+ var VAR_REPLACEMENTS = [
520
+ [
521
+ /var\(\s*--field-note-padding-top\s*(?:,[^)]+)?\)/gi,
522
+ "var(--field-padding-block)"
523
+ ],
524
+ [
525
+ /var\(\s*--field-note-padding\s*(?:,[^)]+)?\)/gi,
526
+ "var(--field-padding-block)"
527
+ ],
528
+ [
529
+ /var\(\s*--field-side-padding\s*(?:,[^)]+)?\)/gi,
530
+ "var(--field-padding-inline)"
531
+ ],
532
+ [
533
+ /var\(\s*--control-side-padding\s*(?:,[^)]+)?\)/gi,
534
+ "var(--field-padding-inline)"
535
+ ],
536
+ [
537
+ /var\(\s*--control-inner-padding\s*(?:,[^)]+)?\)/gi,
538
+ "var(--field-padding)"
539
+ ],
540
+ [
541
+ /var\(\s*--date-popup-shadow\s*(?:,[^)]+)?\)/gi,
542
+ "var(--popup-shadow)"
543
+ ]
544
+ ];
545
+ var DECL_REMOVE = [
546
+ "--field-note-padding-top",
547
+ "--field-note-padding",
548
+ "--control-inner-padding",
549
+ "--control-side-padding",
550
+ "--field-side-padding",
551
+ "--date-popup-shadow"
552
+ ];
642
553
  function migrateDatePopupShadowDecl(css) {
643
554
  let n = 0;
644
555
  let next = css;
@@ -697,6 +608,18 @@ function rewriteFormTokens(source) {
697
608
  next = rem.next;
698
609
  hits += rem.n;
699
610
  }
611
+ {
612
+ const names = new Set;
613
+ const re = /--search-[a-z0-9-]+(?=\s*:)/gi;
614
+ let m;
615
+ while (m = re.exec(next))
616
+ names.add(m[0]);
617
+ for (const name of names) {
618
+ const rem = removeCssDecl(next, name);
619
+ next = rem.next;
620
+ hits += rem.n;
621
+ }
622
+ }
700
623
  const clean = stripOrphanColorMix(next);
701
624
  next = clean.next;
702
625
  hits += clean.hits;
@@ -704,12 +627,12 @@ function rewriteFormTokens(source) {
704
627
  }
705
628
  async function migrateFormTokens(cwd, dry) {
706
629
  const pkg = readPackageJson(cwd);
707
- if (pkg?.name === "@elcrm/form")
630
+ if (pkg?.name === "@elcrm/form" || pkg?.name === "@elcrm/search")
708
631
  return [];
709
632
  const changes = [];
710
633
  const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r)).filter((f) => f.endsWith(".css"));
711
634
  for (const file of files) {
712
- const raw = readFileSync10(file, "utf8");
635
+ const raw = readFileSync5(file, "utf8");
713
636
  const { next, hits } = rewriteFormTokens(raw);
714
637
  if (hits === 0 || next === raw)
715
638
  continue;
@@ -722,10 +645,22 @@ async function migrateFormTokens(cwd, dry) {
722
645
  }
723
646
  return changes;
724
647
  }
648
+ var FORM_TOKEN_DEPRECATED = [
649
+ "--field-note-padding",
650
+ "--field-note-padding-top",
651
+ "--control-inner-padding",
652
+ "--control-side-padding",
653
+ "--field-side-padding",
654
+ "--date-popup-shadow"
655
+ ];
656
+ var DEPRECATED_VAR_RE = new RegExp(`var\\(\\s*(?:${FORM_TOKEN_DEPRECATED.map((n) => n.replace(/-/g, "\\-")).join("|")})\\s*`, "i");
657
+ var DEPRECATED_DECL_RE = new RegExp(`(?:${FORM_TOKEN_DEPRECATED.map((n) => n.replace(/-/g, "\\-")).join("|")})\\s*:`);
725
658
  function lineHasDeprecatedFormToken(line) {
726
659
  const t = line.trim();
727
660
  if (!t || t.startsWith("/*") || t.startsWith("*"))
728
661
  return false;
662
+ if (/(?:var\(\s*)?--search-[a-z0-9-]+/.test(t))
663
+ return true;
729
664
  if (DEPRECATED_VAR_RE.test(t))
730
665
  return true;
731
666
  if (!DEPRECATED_DECL_RE.test(t))
@@ -734,59 +669,186 @@ function lineHasDeprecatedFormToken(line) {
734
669
  return false;
735
670
  return true;
736
671
  }
737
- var VAR_REPLACEMENTS, DECL_REMOVE, FORM_TOKEN_DEPRECATED, DEPRECATED_VAR_RE, DEPRECATED_DECL_RE;
738
- var init_form_tokens = __esm(() => {
739
- init_pkg();
740
- init_walk();
741
- init_css_sanitize();
742
- VAR_REPLACEMENTS = [
743
- [
744
- /var\(\s*--field-note-padding-top\s*(?:,[^)]+)?\)/gi,
745
- "var(--field-padding-block)"
746
- ],
747
- [
748
- /var\(\s*--field-note-padding\s*(?:,[^)]+)?\)/gi,
749
- "var(--field-padding-block)"
750
- ],
751
- [
752
- /var\(\s*--field-side-padding\s*(?:,[^)]+)?\)/gi,
753
- "var(--field-padding-inline)"
754
- ],
755
- [
756
- /var\(\s*--control-side-padding\s*(?:,[^)]+)?\)/gi,
757
- "var(--field-padding-inline)"
758
- ],
759
- [
760
- /var\(\s*--control-inner-padding\s*(?:,[^)]+)?\)/gi,
761
- "var(--field-padding)"
762
- ],
763
- [
764
- /var\(\s*--date-popup-shadow\s*(?:,[^)]+)?\)/gi,
765
- "var(--popup-shadow)"
766
- ]
767
- ];
768
- DECL_REMOVE = [
769
- "--field-note-padding-top",
770
- "--field-note-padding",
771
- "--control-inner-padding",
772
- "--control-side-padding",
773
- "--field-side-padding",
774
- "--date-popup-shadow"
775
- ];
776
- FORM_TOKEN_DEPRECATED = [
777
- "--field-note-padding",
778
- "--field-note-padding-top",
779
- "--control-inner-padding",
780
- "--control-side-padding",
781
- "--field-side-padding",
782
- "--date-popup-shadow"
783
- ];
784
- DEPRECATED_VAR_RE = new RegExp(`var\\(\\s*(?:${FORM_TOKEN_DEPRECATED.map((n) => n.replace(/-/g, "\\-")).join("|")})\\s*`, "i");
785
- DEPRECATED_DECL_RE = new RegExp(`(?:${FORM_TOKEN_DEPRECATED.map((n) => n.replace(/-/g, "\\-")).join("|")})\\s*:`);
786
- });
787
672
 
788
673
  // src/lib/migrate/components.ts
789
- import { readFileSync as readFileSync11 } from "fs";
674
+ import { readFileSync as readFileSync6 } from "fs";
675
+ var VAR_REPLACEMENTS2 = [
676
+ [/var\(\s*--layout-background\s*(?:,[^)]+)?\)/gi, "var(--shell-background-muted)"],
677
+ [/var\(\s*--header-background\s*(?:,[^)]+)?\)/gi, "var(--shell-background)"],
678
+ [/var\(\s*--header-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
679
+ [/var\(\s*--header-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
680
+ [/var\(\s*--footer-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
681
+ [/var\(\s*--footer-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
682
+ [/var\(\s*--item-background-hover\s*(?:,[^)]+)?\)/gi, "var(--shell-background-hover)"],
683
+ [/var\(\s*--item-background-active\s*(?:,[^)]+)?\)/gi, "var(--shell-background-selected)"],
684
+ [/var\(\s*--item-background\s*(?:,[^)]+)?\)/gi, "var(--shell-background)"],
685
+ [/var\(\s*--item-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
686
+ [/var\(\s*--item-color-muted\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
687
+ [/var\(\s*--item-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
688
+ [/var\(\s*--item-gap\s*(?:,[^)]+)?\)/gi, "var(--shell-gap)"],
689
+ [/var\(\s*--card-background\s*(?:,[^)]+)?\)/gi, "var(--shell-background)"],
690
+ [/var\(\s*--card-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
691
+ [/var\(\s*--card-shadow\s*(?:,[^)]+)?\)/gi, "var(--shell-shadow)"],
692
+ [/var\(\s*--card-padding\s*(?:,[^)]+)?\)/gi, "var(--shell-padding)"],
693
+ [/var\(\s*--menu-item-background-hover\s*(?:,[^)]+)?\)/gi, "var(--shell-background-hover)"],
694
+ [/var\(\s*--menu-item-background-active\s*(?:,[^)]+)?\)/gi, "var(--shell-background-selected)"],
695
+ [/var\(\s*--menu-item-color-active\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
696
+ [/var\(\s*--menu-item-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
697
+ [/var\(\s*--loading-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
698
+ [/var\(\s*--section-title-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
699
+ [/var\(\s*--section-description-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
700
+ [/var\(\s*--section-auth-card-background\s*(?:,[^)]+)?\)/gi, "var(--shell-background)"],
701
+ [/var\(\s*--section-auth-card-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
702
+ [/var\(\s*--section-auth-card-shadow\s*(?:,[^)]+)?\)/gi, "var(--shell-shadow)"],
703
+ [/var\(\s*--section-block-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
704
+ [/var\(\s*--nav-sections-label-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
705
+ [/var\(\s*--nav-sections-item-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
706
+ [/var\(\s*--nav-sections-item-background-hover\s*(?:,[^)]+)?\)/gi, "var(--shell-background-hover)"],
707
+ [/var\(\s*--nav-sections-item-background-active\s*(?:,[^)]+)?\)/gi, "var(--shell-background-selected)"],
708
+ [/var\(\s*--nav-sections-accent\s*(?:,[^)]+)?\)/gi, "var(--shell-color-accent)"],
709
+ [/var\(\s*--nav-sections-icon-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
710
+ [/var\(\s*--nav-sections-group-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
711
+ [/var\(\s*--nav-sections-footer-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
712
+ [/var\(\s*--tab-sections-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
713
+ [/var\(\s*--tab-sections-color-active\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
714
+ [/var\(\s*--tab-sections-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
715
+ [/var\(\s*--tab-sections-accent\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
716
+ [/var\(\s*--tab-sections-badge-background\s*(?:,[^)]+)?\)/gi, "var(--shell-color-danger)"],
717
+ [/var\(\s*--brand-color-hover\s*(?:,[^)]+)?\)/gi, "var(--shell-color-accent)"],
718
+ [/var\(\s*--brand-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
719
+ [/var\(\s*--badge-neutral-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
720
+ [/var\(\s*--badge-accent-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-accent)"],
721
+ [/var\(\s*--badge-success-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-success)"],
722
+ [/var\(\s*--badge-danger-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-danger)"],
723
+ [/var\(\s*--list-gap\s*(?:,[^)]+)?\)/gi, "var(--shell-gap-sm)"],
724
+ [/var\(\s*--item-padding\s*(?:,[^)]+)?\)/gi, "var(--shell-padding)"],
725
+ [/var\(\s*--item-radius\s*(?:,[^)]+)?\)/gi, "var(--shell-radius)"],
726
+ [/var\(\s*--block-gap\s*(?:,[^)]+)?\)/gi, "var(--shell-gap)"],
727
+ [/var\(\s*--row-gap\s*(?:,[^)]+)?\)/gi, "var(--shell-gap)"],
728
+ [/var\(\s*--column-gap\s*(?:,[^)]+)?\)/gi, "var(--shell-gap)"],
729
+ [/var\(\s*--page-gap\s*(?:,[^)]+)?\)/gi, "var(--shell-gap)"],
730
+ [/var\(\s*--page-pad\s*(?:,[^)]+)?\)/gi, "var(--shell-padding)"],
731
+ [/var\(\s*--page-pad-x\s*(?:,[^)]+)?\)/gi, "var(--shell-padding-inline)"],
732
+ [/var\(\s*--page-pad-y\s*(?:,[^)]+)?\)/gi, "var(--shell-padding-block)"],
733
+ [/var\(\s*--card-radius\s*(?:,[^)]+)?\)/gi, "var(--shell-radius)"],
734
+ [/var\(\s*--section-description-size\s*(?:,[^)]+)?\)/gi, "0.95rem"],
735
+ [/var\(\s*--section-title-size\s*(?:,[^)]+)?\)/gi, "1.5rem"]
736
+ ];
737
+ var COMPONENTS_TOKEN_DEPRECATED = [
738
+ "--layout-background",
739
+ "--layout-min-height",
740
+ "--layout-gap",
741
+ "--header-background",
742
+ "--header-color",
743
+ "--header-border",
744
+ "--header-padding",
745
+ "--header-gap",
746
+ "--footer-background",
747
+ "--footer-color",
748
+ "--footer-border",
749
+ "--footer-padding",
750
+ "--footer-font-size",
751
+ "--list-gap",
752
+ "--list-padding",
753
+ "--item-background",
754
+ "--item-background-hover",
755
+ "--item-background-active",
756
+ "--item-color",
757
+ "--item-color-muted",
758
+ "--item-border",
759
+ "--item-radius",
760
+ "--item-padding",
761
+ "--item-gap",
762
+ "--card-background",
763
+ "--card-border",
764
+ "--card-shadow",
765
+ "--card-radius",
766
+ "--card-padding",
767
+ "--block-gap",
768
+ "--block-padding",
769
+ "--row-gap",
770
+ "--column-gap",
771
+ "--menu-gap",
772
+ "--menu-item-radius",
773
+ "--menu-item-padding",
774
+ "--menu-item-background",
775
+ "--menu-item-background-hover",
776
+ "--menu-item-background-active",
777
+ "--menu-item-color",
778
+ "--menu-item-color-active",
779
+ "--loading-padding",
780
+ "--loading-color",
781
+ "--section-gap",
782
+ "--section-description-size",
783
+ "--section-icon-gap",
784
+ "--section-body-gap",
785
+ "--section-actions-gap",
786
+ "--section-auth-padding",
787
+ "--section-auth-card-padding",
788
+ "--section-auth-card-radius",
789
+ "--section-block-padding-bottom",
790
+ "--section-block-margin-bottom",
791
+ "--section-title-color",
792
+ "--section-description-color",
793
+ "--section-auth-card-background",
794
+ "--section-auth-card-border",
795
+ "--section-auth-card-shadow",
796
+ "--section-block-border",
797
+ "--nav-sections-gap",
798
+ "--nav-sections-item-radius",
799
+ "--nav-sections-item-padding",
800
+ "--nav-sections-label-color",
801
+ "--nav-sections-item-color",
802
+ "--nav-sections-item-background-hover",
803
+ "--nav-sections-item-background-active",
804
+ "--nav-sections-accent",
805
+ "--nav-sections-icon-color",
806
+ "--nav-sections-group-border",
807
+ "--nav-sections-footer-border",
808
+ "--tab-sections-gap",
809
+ "--tab-sections-tab-padding",
810
+ "--tab-sections-tab-gap",
811
+ "--tab-sections-badge-size",
812
+ "--tab-sections-border",
813
+ "--tab-sections-color",
814
+ "--tab-sections-color-active",
815
+ "--tab-sections-accent",
816
+ "--tab-sections-badge-background",
817
+ "--tab-sections-badge-color",
818
+ "--badge-padding",
819
+ "--badge-radius",
820
+ "--badge-font-size",
821
+ "--badge-font-weight",
822
+ "--badge-border",
823
+ "--badge-neutral-background",
824
+ "--badge-neutral-color",
825
+ "--badge-accent-background",
826
+ "--badge-accent-color",
827
+ "--badge-success-background",
828
+ "--badge-success-color",
829
+ "--badge-warning-background",
830
+ "--badge-warning-color",
831
+ "--badge-danger-background",
832
+ "--badge-danger-color",
833
+ "--brand-font-weight",
834
+ "--brand-letter-spacing",
835
+ "--brand-color",
836
+ "--brand-color-hover",
837
+ "--empty-state-gap",
838
+ "--empty-state-padding",
839
+ "--empty-state-icon-gap",
840
+ "--empty-state-title-size",
841
+ "--empty-state-description-size",
842
+ "--empty-state-max-width",
843
+ "--empty-state-actions-gap",
844
+ "--empty-state-actions-margin-top",
845
+ "--radio-group-gap",
846
+ "--page-pad",
847
+ "--page-pad-x",
848
+ "--page-pad-y",
849
+ "--page-gap",
850
+ "--card-radius"
851
+ ];
790
852
  function rewriteComponentsCss(source) {
791
853
  let next = source;
792
854
  let hits = 0;
@@ -806,7 +868,7 @@ function rewriteComponentsCss(source) {
806
868
  }
807
869
  return { next, hits };
808
870
  }
809
- function rewriteImports2(source) {
871
+ function rewriteImports(source) {
810
872
  let hits = 0;
811
873
  let next = source;
812
874
  const replaceImportList = (list) => {
@@ -928,7 +990,7 @@ function rewriteItemKeys(source) {
928
990
  function rewriteComponentsTsx(source) {
929
991
  let hits = 0;
930
992
  let next = source;
931
- const imp = rewriteImports2(next);
993
+ const imp = rewriteImports(next);
932
994
  next = imp.next;
933
995
  hits += imp.hits;
934
996
  const jsx = rewriteJsx(next);
@@ -952,7 +1014,7 @@ async function migrateComponents(cwd, dry) {
952
1014
  const changes = [];
953
1015
  const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r));
954
1016
  for (const file of files) {
955
- const raw = readFileSync11(file, "utf8");
1017
+ const raw = readFileSync6(file, "utf8");
956
1018
  let next = raw;
957
1019
  let hits = 0;
958
1020
  if (file.endsWith(".css")) {
@@ -978,6 +1040,8 @@ async function migrateComponents(cwd, dry) {
978
1040
  }
979
1041
  return changes;
980
1042
  }
1043
+ var DEPRECATED_VAR_RE2 = new RegExp(`var\\(\\s*(?:${COMPONENTS_TOKEN_DEPRECATED.map((n) => n.replace(/-/g, "\\-")).join("|")})(?![\\w-])`, "i");
1044
+ var DEPRECATED_DECL_RE2 = new RegExp(`(?:${COMPONENTS_TOKEN_DEPRECATED.map((n) => n.replace(/-/g, "\\-")).join("|")})(?![\\w-])\\s*:`);
981
1045
  function lineHasDeprecatedComponentsToken(line) {
982
1046
  const t = line.trim();
983
1047
  if (!t || t.startsWith("/*") || t.startsWith("*"))
@@ -990,407 +1054,8 @@ function lineHasDeprecatedComponentsToken(line) {
990
1054
  return false;
991
1055
  return true;
992
1056
  }
993
- var VAR_REPLACEMENTS2, COMPONENTS_TOKEN_DEPRECATED, DEPRECATED_VAR_RE2, DEPRECATED_DECL_RE2;
994
- var init_components = __esm(() => {
995
- init_pkg();
996
- init_walk();
997
- init_css_sanitize();
998
- VAR_REPLACEMENTS2 = [
999
- [/var\(\s*--layout-background\s*(?:,[^)]+)?\)/gi, "var(--shell-background-muted)"],
1000
- [/var\(\s*--header-background\s*(?:,[^)]+)?\)/gi, "var(--shell-background)"],
1001
- [/var\(\s*--header-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
1002
- [/var\(\s*--header-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
1003
- [/var\(\s*--footer-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
1004
- [/var\(\s*--footer-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
1005
- [/var\(\s*--item-background-hover\s*(?:,[^)]+)?\)/gi, "var(--shell-background-hover)"],
1006
- [/var\(\s*--item-background-active\s*(?:,[^)]+)?\)/gi, "var(--shell-background-selected)"],
1007
- [/var\(\s*--item-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
1008
- [/var\(\s*--card-background\s*(?:,[^)]+)?\)/gi, "var(--shell-background)"],
1009
- [/var\(\s*--card-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
1010
- [/var\(\s*--card-shadow\s*(?:,[^)]+)?\)/gi, "var(--shell-shadow)"],
1011
- [/var\(\s*--menu-item-background-hover\s*(?:,[^)]+)?\)/gi, "var(--shell-background-hover)"],
1012
- [/var\(\s*--menu-item-background-active\s*(?:,[^)]+)?\)/gi, "var(--shell-background-selected)"],
1013
- [/var\(\s*--menu-item-color-active\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
1014
- [/var\(\s*--menu-item-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
1015
- [/var\(\s*--loading-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
1016
- [/var\(\s*--section-title-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
1017
- [/var\(\s*--section-description-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
1018
- [/var\(\s*--section-auth-card-background\s*(?:,[^)]+)?\)/gi, "var(--shell-background)"],
1019
- [/var\(\s*--section-auth-card-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
1020
- [/var\(\s*--section-auth-card-shadow\s*(?:,[^)]+)?\)/gi, "var(--shell-shadow)"],
1021
- [/var\(\s*--section-block-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
1022
- [/var\(\s*--nav-sections-label-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
1023
- [/var\(\s*--nav-sections-item-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
1024
- [/var\(\s*--nav-sections-item-background-hover\s*(?:,[^)]+)?\)/gi, "var(--shell-background-hover)"],
1025
- [/var\(\s*--nav-sections-item-background-active\s*(?:,[^)]+)?\)/gi, "var(--shell-background-selected)"],
1026
- [/var\(\s*--nav-sections-accent\s*(?:,[^)]+)?\)/gi, "var(--shell-color-accent)"],
1027
- [/var\(\s*--nav-sections-icon-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
1028
- [/var\(\s*--nav-sections-group-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
1029
- [/var\(\s*--nav-sections-footer-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
1030
- [/var\(\s*--tab-sections-border\s*(?:,[^)]+)?\)/gi, "var(--shell-border)"],
1031
- [/var\(\s*--tab-sections-color-active\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
1032
- [/var\(\s*--tab-sections-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-muted)"],
1033
- [/var\(\s*--tab-sections-accent\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
1034
- [/var\(\s*--tab-sections-badge-background\s*(?:,[^)]+)?\)/gi, "var(--shell-color-danger)"],
1035
- [/var\(\s*--brand-color-hover\s*(?:,[^)]+)?\)/gi, "var(--shell-color-accent)"],
1036
- [/var\(\s*--brand-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
1037
- [/var\(\s*--badge-neutral-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color)"],
1038
- [/var\(\s*--badge-accent-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-accent)"],
1039
- [/var\(\s*--badge-success-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-success)"],
1040
- [/var\(\s*--badge-danger-color\s*(?:,[^)]+)?\)/gi, "var(--shell-color-danger)"],
1041
- [/var\(\s*--list-gap\s*(?:,[^)]+)?\)/gi, "var(--shell-gap-sm)"],
1042
- [/var\(\s*--item-padding\s*(?:,[^)]+)?\)/gi, "var(--shell-padding)"],
1043
- [/var\(\s*--item-radius\s*(?:,[^)]+)?\)/gi, "var(--shell-radius)"],
1044
- [/var\(\s*--block-gap\s*(?:,[^)]+)?\)/gi, "var(--shell-gap)"],
1045
- [/var\(\s*--row-gap\s*(?:,[^)]+)?\)/gi, "var(--shell-gap)"],
1046
- [/var\(\s*--column-gap\s*(?:,[^)]+)?\)/gi, "var(--shell-gap)"],
1047
- [/var\(\s*--page-gap\s*(?:,[^)]+)?\)/gi, "var(--shell-gap)"],
1048
- [/var\(\s*--page-pad\s*(?:,[^)]+)?\)/gi, "var(--shell-padding)"],
1049
- [/var\(\s*--page-pad-x\s*(?:,[^)]+)?\)/gi, "var(--shell-padding-inline)"],
1050
- [/var\(\s*--page-pad-y\s*(?:,[^)]+)?\)/gi, "var(--shell-padding-block)"],
1051
- [/var\(\s*--card-radius\s*(?:,[^)]+)?\)/gi, "var(--shell-radius)"],
1052
- [/var\(\s*--section-description-size\s*(?:,[^)]+)?\)/gi, "0.95rem"],
1053
- [/var\(\s*--section-title-size\s*(?:,[^)]+)?\)/gi, "1.5rem"]
1054
- ];
1055
- COMPONENTS_TOKEN_DEPRECATED = [
1056
- "--layout-background",
1057
- "--layout-min-height",
1058
- "--layout-gap",
1059
- "--header-background",
1060
- "--header-color",
1061
- "--header-border",
1062
- "--header-padding",
1063
- "--header-gap",
1064
- "--footer-background",
1065
- "--footer-color",
1066
- "--footer-border",
1067
- "--footer-padding",
1068
- "--footer-font-size",
1069
- "--list-gap",
1070
- "--list-padding",
1071
- "--item-background",
1072
- "--item-background-hover",
1073
- "--item-background-active",
1074
- "--item-color",
1075
- "--item-color-muted",
1076
- "--item-border",
1077
- "--item-radius",
1078
- "--item-padding",
1079
- "--item-gap",
1080
- "--card-background",
1081
- "--card-border",
1082
- "--card-shadow",
1083
- "--card-radius",
1084
- "--card-padding",
1085
- "--block-gap",
1086
- "--block-padding",
1087
- "--row-gap",
1088
- "--column-gap",
1089
- "--menu-gap",
1090
- "--menu-item-radius",
1091
- "--menu-item-padding",
1092
- "--menu-item-background",
1093
- "--menu-item-background-hover",
1094
- "--menu-item-background-active",
1095
- "--menu-item-color",
1096
- "--menu-item-color-active",
1097
- "--loading-padding",
1098
- "--loading-color",
1099
- "--section-gap",
1100
- "--section-description-size",
1101
- "--section-icon-gap",
1102
- "--section-body-gap",
1103
- "--section-actions-gap",
1104
- "--section-auth-padding",
1105
- "--section-auth-card-padding",
1106
- "--section-auth-card-radius",
1107
- "--section-block-padding-bottom",
1108
- "--section-block-margin-bottom",
1109
- "--section-title-color",
1110
- "--section-description-color",
1111
- "--section-auth-card-background",
1112
- "--section-auth-card-border",
1113
- "--section-auth-card-shadow",
1114
- "--section-block-border",
1115
- "--nav-sections-gap",
1116
- "--nav-sections-item-radius",
1117
- "--nav-sections-item-padding",
1118
- "--nav-sections-label-color",
1119
- "--nav-sections-item-color",
1120
- "--nav-sections-item-background-hover",
1121
- "--nav-sections-item-background-active",
1122
- "--nav-sections-accent",
1123
- "--nav-sections-icon-color",
1124
- "--nav-sections-group-border",
1125
- "--nav-sections-footer-border",
1126
- "--tab-sections-gap",
1127
- "--tab-sections-tab-padding",
1128
- "--tab-sections-tab-gap",
1129
- "--tab-sections-badge-size",
1130
- "--tab-sections-border",
1131
- "--tab-sections-color",
1132
- "--tab-sections-color-active",
1133
- "--tab-sections-accent",
1134
- "--tab-sections-badge-background",
1135
- "--tab-sections-badge-color",
1136
- "--badge-padding",
1137
- "--badge-radius",
1138
- "--badge-font-size",
1139
- "--badge-font-weight",
1140
- "--badge-border",
1141
- "--badge-neutral-background",
1142
- "--badge-neutral-color",
1143
- "--badge-accent-background",
1144
- "--badge-accent-color",
1145
- "--badge-success-background",
1146
- "--badge-success-color",
1147
- "--badge-warning-background",
1148
- "--badge-warning-color",
1149
- "--badge-danger-background",
1150
- "--badge-danger-color",
1151
- "--brand-font-weight",
1152
- "--brand-letter-spacing",
1153
- "--brand-color",
1154
- "--brand-color-hover",
1155
- "--empty-state-gap",
1156
- "--empty-state-padding",
1157
- "--empty-state-icon-gap",
1158
- "--empty-state-title-size",
1159
- "--empty-state-description-size",
1160
- "--empty-state-max-width",
1161
- "--empty-state-actions-gap",
1162
- "--empty-state-actions-margin-top",
1163
- "--radio-group-gap",
1164
- "--page-pad",
1165
- "--page-pad-x",
1166
- "--page-pad-y",
1167
- "--page-gap",
1168
- "--card-radius"
1169
- ];
1170
- DEPRECATED_VAR_RE2 = new RegExp(`var\\(\\s*(?:${COMPONENTS_TOKEN_DEPRECATED.map((n) => n.replace(/-/g, "\\-")).join("|")})(?![\\w-])`, "i");
1171
- DEPRECATED_DECL_RE2 = new RegExp(`(?:${COMPONENTS_TOKEN_DEPRECATED.map((n) => n.replace(/-/g, "\\-")).join("|")})(?![\\w-])\\s*:`);
1172
- });
1173
-
1174
- // src/lib/migrate/front.ts
1175
- var exports_front = {};
1176
- __export(exports_front, {
1177
- migrateFrontAll: () => migrateFrontAll,
1178
- FRONT_MIGRATIONS: () => FRONT_MIGRATIONS,
1179
- FRONT_MIGRATE_ORDER: () => FRONT_MIGRATE_ORDER
1180
- });
1181
- async function migrateFrontAll(cwd, dry) {
1182
- const all = [];
1183
- for (const name of FRONT_MIGRATE_ORDER) {
1184
- const part = await FRONT_MIGRATIONS[name](cwd, dry);
1185
- for (const c2 of part) {
1186
- all.push({ file: c2.file, detail: `[${name}] ${c2.detail}` });
1187
- }
1188
- }
1189
- return all;
1190
- }
1191
- var FRONT_MIGRATIONS, FRONT_MIGRATE_ORDER;
1192
- var init_front = __esm(() => {
1193
- init_socket_server();
1194
- init_size_sml();
1195
- init_field_border();
1196
- init_modal_create();
1197
- init_legacy_hacks();
1198
- init_form_aliases();
1199
- init_form_tokens();
1200
- init_components();
1201
- init_css_sanitize();
1202
- FRONT_MIGRATIONS = {
1203
- "size-sml": migrateSizeSml,
1204
- "field-border": migrateFieldBorder,
1205
- "modal-create": migrateModalCreate,
1206
- "form-aliases": migrateFormAliases,
1207
- "form-tokens": migrateFormTokens,
1208
- components: migrateComponents,
1209
- "css-sanitize": migrateCssSanitize,
1210
- "legacy-hacks": migrateLegacyReactHack,
1211
- "socket-server": migrateSocketServer
1212
- };
1213
- FRONT_MIGRATE_ORDER = [
1214
- "form-aliases",
1215
- "form-tokens",
1216
- "components",
1217
- "css-sanitize",
1218
- "size-sml",
1219
- "field-border",
1220
- "modal-create",
1221
- "socket-server"
1222
- ];
1223
- });
1224
-
1225
- // src/index.ts
1226
- import { readFileSync as readFileSync21 } from "fs";
1227
- import { dirname as dirname4, join as join18 } from "path";
1228
- import { fileURLToPath as fileURLToPath2 } from "url";
1229
-
1230
- // src/parseArgs.ts
1231
- function parseArgs(argv = process.argv.slice(2)) {
1232
- const options = {};
1233
- const flags = [];
1234
- const positionals = [];
1235
- for (let i = 0;i < argv.length; i++) {
1236
- const arg = argv[i];
1237
- if (arg === "--") {
1238
- positionals.push(...argv.slice(i + 1));
1239
- break;
1240
- }
1241
- if (arg.startsWith("--")) {
1242
- const body = arg.slice(2);
1243
- if (body.includes("=")) {
1244
- const eq = body.indexOf("=");
1245
- const key = body.slice(0, eq);
1246
- const value = body.slice(eq + 1);
1247
- if (key)
1248
- options[key] = value;
1249
- continue;
1250
- }
1251
- const next = argv[i + 1];
1252
- if (next && !next.startsWith("-")) {
1253
- const valueOpts = new Set([
1254
- "template",
1255
- "url",
1256
- "name",
1257
- "dir",
1258
- "folder",
1259
- "out"
1260
- ]);
1261
- if (valueOpts.has(body)) {
1262
- options[body] = next;
1263
- i++;
1264
- continue;
1265
- }
1266
- }
1267
- if (body && !flags.includes(body))
1268
- flags.push(body);
1269
- continue;
1270
- }
1271
- if (arg.startsWith("-") && arg.length > 1) {
1272
- const short = arg.slice(1);
1273
- if (short === "t" && argv[i + 1] && !argv[i + 1].startsWith("-")) {
1274
- options.template = argv[i + 1];
1275
- i++;
1276
- continue;
1277
- }
1278
- if (short && !flags.includes(short))
1279
- flags.push(short);
1280
- continue;
1281
- }
1282
- positionals.push(arg);
1283
- }
1284
- const [command = "", ...rest] = positionals;
1285
- return {
1286
- command,
1287
- positionals: rest,
1288
- options,
1289
- flags
1290
- };
1291
- }
1292
- function hasFlag(parsed, ...names) {
1293
- return names.some((n) => parsed.flags.includes(n));
1294
- }
1295
-
1296
- // src/lib/elcrmDeps.ts
1297
- var SECTIONS = [
1298
- "dependencies",
1299
- "devDependencies",
1300
- "peerDependencies"
1301
- ];
1302
- function listElcrmDeps(pkg) {
1303
- const out = [];
1304
- for (const section of SECTIONS) {
1305
- const map = pkg[section];
1306
- if (!map || typeof map !== "object")
1307
- continue;
1308
- for (const [name, version] of Object.entries(map)) {
1309
- if (name.startsWith("@elcrm/") && typeof version === "string") {
1310
- out.push({ name, version, section });
1311
- }
1312
- }
1313
- }
1314
- return out;
1315
- }
1316
- function uniqueElcrmNames(deps) {
1317
- return [...new Set(deps.map((d) => d.name))].sort();
1318
- }
1319
-
1320
- // src/commands/update.ts
1321
- init_latest();
1322
-
1323
- // src/lib/install.ts
1324
- import { existsSync } from "fs";
1325
- import { join } from "path";
1326
- import { spawnSync } from "child_process";
1327
- function detectPackageManager(cwd = process.cwd()) {
1328
- if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) {
1329
- return "bun";
1330
- }
1331
- const bun = spawnSync("bun", ["--version"], { encoding: "utf8" });
1332
- if (bun.status === 0)
1333
- return "bun";
1334
- return "npm";
1335
- }
1336
- function installDeps(cwd = process.cwd()) {
1337
- const pm = detectPackageManager(cwd);
1338
- console.log(`\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439 (${pm} install)\u2026`);
1339
- const r = spawnSync(pm, ["install"], {
1340
- cwd,
1341
- stdio: "inherit",
1342
- env: process.env
1343
- });
1344
- return r.status === 0;
1345
- }
1346
- function installGlobalElcrm() {
1347
- const bun = spawnSync("bun", ["--version"], { encoding: "utf8" });
1348
- const cmd = bun.status === 0 ? "bun" : "npm";
1349
- const args = ["i", "-g", "elcrm@latest"];
1350
- console.log(`CLI: ${cmd} ${args.join(" ")}`);
1351
- const r = spawnSync(cmd, args, {
1352
- stdio: "inherit",
1353
- env: process.env
1354
- });
1355
- return r.status === 0;
1356
- }
1357
-
1358
- // src/commands/update.ts
1359
- init_pkg();
1360
-
1361
- // src/lib/ansi.ts
1362
- var enabled = process.env.NO_COLOR == null && process.env.FORCE_COLOR !== "0" && Boolean(process.stdout.isTTY);
1363
- function wrap(code, text) {
1364
- if (!enabled)
1365
- return text;
1366
- return `\x1B[${code}m${text}\x1B[0m`;
1367
- }
1368
- var c = {
1369
- gray: (t) => wrap("90", t),
1370
- green: (t) => wrap("32", t),
1371
- blue: (t) => wrap("34", t),
1372
- yellow: (t) => wrap("33", t),
1373
- red: (t) => wrap("31", t),
1374
- cyan: (t) => wrap("36", t),
1375
- dim: (t) => wrap("2", t)
1376
- };
1377
-
1378
- // src/commands/update.ts
1379
- init_front();
1380
- init_css_sanitize();
1381
1057
 
1382
1058
  // src/lib/cssSync.ts
1383
- init_form_tokens();
1384
- init_components();
1385
- import {
1386
- existsSync as existsSync6,
1387
- readdirSync as readdirSync3,
1388
- readFileSync as readFileSync12,
1389
- statSync as statSync3,
1390
- writeFileSync as writeFileSync4
1391
- } from "fs";
1392
- import { createRequire } from "module";
1393
- import { dirname, join as join6 } from "path";
1394
1059
  var TOKEN_DEPRECATED = new Set([
1395
1060
  ...FORM_TOKEN_DEPRECATED,
1396
1061
  ...COMPONENTS_TOKEN_DEPRECATED
@@ -1483,12 +1148,12 @@ function isColorToken(name, value) {
1483
1148
  return false;
1484
1149
  }
1485
1150
  function listCssFiles(dir, acc = []) {
1486
- if (!existsSync6(dir))
1151
+ if (!existsSync5(dir))
1487
1152
  return acc;
1488
1153
  for (const name of readdirSync3(dir)) {
1489
1154
  if (name === "node_modules")
1490
1155
  continue;
1491
- const full = join6(dir, name);
1156
+ const full = join5(dir, name);
1492
1157
  let st;
1493
1158
  try {
1494
1159
  st = statSync3(full);
@@ -1504,17 +1169,17 @@ function listCssFiles(dir, acc = []) {
1504
1169
  }
1505
1170
  function resolvePkgRoot(pkgName, cwd) {
1506
1171
  try {
1507
- const req = createRequire(join6(cwd, "package.json"));
1172
+ const req = createRequire(join5(cwd, "package.json"));
1508
1173
  const pkgJson = req.resolve(`${pkgName}/package.json`);
1509
1174
  return dirname(pkgJson);
1510
1175
  } catch {
1511
- const fallback = join6(cwd, "node_modules", ...pkgName.split("/"));
1512
- return existsSync6(join6(fallback, "package.json")) ? fallback : null;
1176
+ const fallback = join5(cwd, "node_modules", ...pkgName.split("/"));
1177
+ return existsSync5(join5(fallback, "package.json")) ? fallback : null;
1513
1178
  }
1514
1179
  }
1515
1180
  function readPkgJson(dir) {
1516
1181
  try {
1517
- return JSON.parse(readFileSync12(join6(dir, "package.json"), "utf8"));
1182
+ return JSON.parse(readFileSync7(join5(dir, "package.json"), "utf8"));
1518
1183
  } catch {
1519
1184
  return null;
1520
1185
  }
@@ -1538,11 +1203,11 @@ function inspectPkgCss(pkgName, cwd) {
1538
1203
  return null;
1539
1204
  const pkg = readPkgJson(root);
1540
1205
  const exp = pkg?.exports;
1541
- const abs = (rel2) => rel2 ? join6(root, rel2.replace(/^\.\//, "")) : undefined;
1206
+ const abs = (rel2) => rel2 ? join5(root, rel2.replace(/^\.\//, "")) : undefined;
1542
1207
  const fileIfExists = (key) => {
1543
1208
  const rel2 = exportPath(exp, key);
1544
1209
  const a = abs(rel2);
1545
- return a && existsSync6(a) ? a : undefined;
1210
+ return a && existsSync5(a) ? a : undefined;
1546
1211
  };
1547
1212
  return {
1548
1213
  name: pkgName,
@@ -1567,8 +1232,8 @@ function resolveElcrmCssImport(spec, cwd) {
1567
1232
  const rel2 = exportPath(pkg?.exports, `./${sub}`);
1568
1233
  if (!rel2)
1569
1234
  return null;
1570
- const abs = join6(root, rel2.replace(/^\.\//, ""));
1571
- return existsSync6(abs) ? abs : null;
1235
+ const abs = join5(root, rel2.replace(/^\.\//, ""));
1236
+ return existsSync5(abs) ? abs : null;
1572
1237
  }
1573
1238
  function pruneBrokenElcrmImports(css, cwd) {
1574
1239
  const removed = [];
@@ -1594,15 +1259,15 @@ function collectPkgTokens(pkgName, cwd) {
1594
1259
  if (!root)
1595
1260
  return [];
1596
1261
  const files = [
1597
- ...listCssFiles(join6(root, "dist")),
1598
- ...listCssFiles(join6(root, "src/lib/styles")),
1599
- ...listCssFiles(join6(root, "src/style")),
1600
- join6(root, "test/main.css")
1601
- ].filter((f) => existsSync6(f));
1262
+ ...listCssFiles(join5(root, "dist")),
1263
+ ...listCssFiles(join5(root, "src/lib/styles")),
1264
+ ...listCssFiles(join5(root, "src/style")),
1265
+ join5(root, "test/main.css")
1266
+ ].filter((f) => existsSync5(f));
1602
1267
  const acc = [];
1603
1268
  const seen = new Set;
1604
1269
  for (const file of files) {
1605
- const css = readFileSync12(file, "utf8");
1270
+ const css = readFileSync7(file, "utf8");
1606
1271
  const kind = fileKind(file);
1607
1272
  if (kind !== "mixed") {
1608
1273
  for (const [name, value] of parseCustomProps(css)) {
@@ -1705,8 +1370,8 @@ ${snippet}${after}`;
1705
1370
  return null;
1706
1371
  }
1707
1372
  function findStyleDir(cwd) {
1708
- for (const d of [join6(cwd, "src/style"), join6(cwd, "web/src/style")]) {
1709
- if (existsSync6(join6(d, "theme-light.css")) || existsSync6(join6(d, "elcrm.css")) || existsSync6(join6(d, "theme.css"))) {
1373
+ for (const d of [join5(cwd, "src/style"), join5(cwd, "web/src/style")]) {
1374
+ if (existsSync5(join5(d, "theme-light.css")) || existsSync5(join5(d, "elcrm.css")) || existsSync5(join5(d, "theme.css"))) {
1710
1375
  return d;
1711
1376
  }
1712
1377
  }
@@ -1741,10 +1406,10 @@ function syncElcrmCss(options) {
1741
1406
  throw new Error("\u041D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D src/style (theme.css / theme-light.css / elcrm.css).");
1742
1407
  }
1743
1408
  const paths = {
1744
- geometry: join6(styleDir, "theme.css"),
1745
- light: join6(styleDir, "theme-light.css"),
1746
- dark: join6(styleDir, "theme-dark.css"),
1747
- elcrm: join6(styleDir, "elcrm.css")
1409
+ geometry: join5(styleDir, "theme.css"),
1410
+ light: join5(styleDir, "theme-light.css"),
1411
+ dark: join5(styleDir, "theme-dark.css"),
1412
+ elcrm: join5(styleDir, "elcrm.css")
1748
1413
  };
1749
1414
  const allDefs = [];
1750
1415
  const skippedPkgs = [];
@@ -1769,11 +1434,11 @@ function syncElcrmCss(options) {
1769
1434
  const labelFor = (bucket) => bucket === "geometry" ? "theme.css" : bucket === "light" ? "theme-light.css" : "theme-dark.css";
1770
1435
  for (const bucket of ["geometry", "light", "dark"]) {
1771
1436
  const file = fileFor(bucket);
1772
- if (!existsSync6(file))
1437
+ if (!existsSync5(file))
1773
1438
  continue;
1774
- let css = readFileSync12(file, "utf8");
1439
+ let css = readFileSync7(file, "utf8");
1775
1440
  const have = existingTokenNames(css);
1776
- const missing = allDefs.filter((d) => d.bucket === bucket && !have.has(d.name) && !TOKEN_DEPRECATED.has(d.name));
1441
+ const missing = allDefs.filter((d) => d.bucket === bucket && !have.has(d.name) && !d.name.startsWith("--search-") && !TOKEN_DEPRECATED.has(d.name));
1777
1442
  const uniq = [];
1778
1443
  const seen = new Set;
1779
1444
  for (const d of missing) {
@@ -1793,10 +1458,10 @@ function syncElcrmCss(options) {
1793
1458
  }
1794
1459
  added[labelFor(bucket)] = uniq.map((d) => d.name);
1795
1460
  if (!options.dry)
1796
- writeFileSync4(file, next, "utf8");
1461
+ writeFileSync3(file, next, "utf8");
1797
1462
  }
1798
- if (existsSync6(paths.elcrm)) {
1799
- let css = readFileSync12(paths.elcrm, "utf8");
1463
+ if (existsSync5(paths.elcrm)) {
1464
+ let css = readFileSync7(paths.elcrm, "utf8");
1800
1465
  const pruned = pruneBrokenElcrmImports(css, options.cwd);
1801
1466
  css = pruned.css;
1802
1467
  removedImports = pruned.removed;
@@ -1816,83 +1481,953 @@ function syncElcrmCss(options) {
1816
1481
  }
1817
1482
  }
1818
1483
  if ((imports.length || removedImports.length) && !options.dry) {
1819
- writeFileSync4(paths.elcrm, css, "utf8");
1484
+ writeFileSync3(paths.elcrm, css, "utf8");
1485
+ }
1486
+ }
1487
+ return { styleDir, added, imports, removedImports, skippedPkgs };
1488
+ }
1489
+
1490
+ // src/lib/migrate/socket-server.ts
1491
+ import {
1492
+ existsSync as existsSync6,
1493
+ readdirSync as readdirSync4,
1494
+ readFileSync as readFileSync8,
1495
+ statSync as statSync4,
1496
+ writeFileSync as writeFileSync4
1497
+ } from "fs";
1498
+ import { join as join6 } from "path";
1499
+ var SKIP_DIRS2 = new Set([
1500
+ "node_modules",
1501
+ "dist",
1502
+ ".git",
1503
+ "coverage",
1504
+ ".turbo"
1505
+ ]);
1506
+ var TEXT_EXT = new Set([
1507
+ ".ts",
1508
+ ".tsx",
1509
+ ".js",
1510
+ ".jsx",
1511
+ ".mjs",
1512
+ ".cjs",
1513
+ ".mts",
1514
+ ".cts"
1515
+ ]);
1516
+ function walkFiles(dir, out = []) {
1517
+ if (!existsSync6(dir))
1518
+ return out;
1519
+ for (const name of readdirSync4(dir)) {
1520
+ if (SKIP_DIRS2.has(name))
1521
+ continue;
1522
+ const full = join6(dir, name);
1523
+ const st = statSync4(full);
1524
+ if (st.isDirectory())
1525
+ walkFiles(full, out);
1526
+ else {
1527
+ const ext = name.includes(".") ? `.${name.split(".").pop()}` : "";
1528
+ if (TEXT_EXT.has(ext))
1529
+ out.push(full);
1530
+ }
1531
+ }
1532
+ return out;
1533
+ }
1534
+ function rewriteImports2(source) {
1535
+ let hits = 0;
1536
+ let next = source;
1537
+ const patterns = [
1538
+ [
1539
+ /from\s+["']@elcrm\/socket\/server["']/g,
1540
+ 'from "@elcrm/server"'
1541
+ ],
1542
+ [
1543
+ /from\s+["']@elcrm\/socket-server["']/g,
1544
+ 'from "@elcrm/server"'
1545
+ ],
1546
+ [
1547
+ /require\s*\(\s*["']@elcrm\/socket\/server["']\s*\)/g,
1548
+ 'require("@elcrm/server")'
1549
+ ],
1550
+ [
1551
+ /require\s*\(\s*["']@elcrm\/socket-server["']\s*\)/g,
1552
+ 'require("@elcrm/server")'
1553
+ ]
1554
+ ];
1555
+ for (const [re, repl] of patterns) {
1556
+ const before = next;
1557
+ next = next.replace(re, () => {
1558
+ hits++;
1559
+ return repl;
1560
+ });
1561
+ }
1562
+ return { next, hits };
1563
+ }
1564
+ async function migrateSocketServer(cwd, dry) {
1565
+ const changes = [];
1566
+ const pkg = readPackageJson(cwd);
1567
+ let removedSocketServer = false;
1568
+ if (pkg) {
1569
+ for (const section of [
1570
+ "dependencies",
1571
+ "devDependencies",
1572
+ "peerDependencies"
1573
+ ]) {
1574
+ const map = pkg[section];
1575
+ if (!map)
1576
+ continue;
1577
+ if (map["@elcrm/socket-server"]) {
1578
+ removedSocketServer = true;
1579
+ changes.push({
1580
+ file: "package.json",
1581
+ detail: `\u0443\u0434\u0430\u043B\u0438\u0442\u044C ${section}["@elcrm/socket-server"]`
1582
+ });
1583
+ if (!dry)
1584
+ delete map["@elcrm/socket-server"];
1585
+ }
1586
+ }
1587
+ const hasServer = Boolean(pkg.dependencies?.["@elcrm/server"]) || Boolean(pkg.devDependencies?.["@elcrm/server"]);
1588
+ if (removedSocketServer && !hasServer) {
1589
+ const latest = dry ? null : await fetchLatestVersion("@elcrm/server");
1590
+ const ver = latest ? `^${latest}` : "^0.5.3";
1591
+ changes.push({
1592
+ file: "package.json",
1593
+ detail: `\u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C dependencies["@elcrm/server"] = ${ver}`
1594
+ });
1595
+ if (!dry) {
1596
+ if (!pkg.dependencies)
1597
+ pkg.dependencies = {};
1598
+ pkg.dependencies["@elcrm/server"] = ver;
1599
+ }
1600
+ }
1601
+ if (!dry && changes.some((c2) => c2.file === "package.json")) {
1602
+ writePackageJson(pkg, cwd);
1603
+ }
1604
+ }
1605
+ const roots = ["src", "app", "server", "lib"].map((d) => join6(cwd, d));
1606
+ const files = roots.flatMap((r) => walkFiles(r));
1607
+ for (const file of files) {
1608
+ const raw = readFileSync8(file, "utf8");
1609
+ const { next, hits } = rewriteImports2(raw);
1610
+ if (hits > 0) {
1611
+ changes.push({
1612
+ file: file.replace(cwd + "/", ""),
1613
+ detail: `${hits} \u0437\u0430\u043C\u0435\u043D(\u044B) \u0438\u043C\u043F\u043E\u0440\u0442\u043E\u0432 \u2192 @elcrm/server`
1614
+ });
1615
+ if (!dry)
1616
+ writeFileSync4(file, next, "utf8");
1617
+ }
1618
+ }
1619
+ return changes;
1620
+ }
1621
+
1622
+ // src/lib/migrate/size-sml.ts
1623
+ import { readFileSync as readFileSync9 } from "fs";
1624
+ var PATTERNS = [
1625
+ [/size\s*=\s*"sm"/g, 'size="s"'],
1626
+ [/size\s*=\s*'sm'/g, "size='s'"],
1627
+ [/size\s*=\s*\{\s*"sm"\s*\}/g, 'size={"s"}'],
1628
+ [/size\s*=\s*\{\s*'sm'\s*\}/g, "size={'s'}"],
1629
+ [/size\s*=\s*"md"/g, 'size="m"'],
1630
+ [/size\s*=\s*'md'/g, "size='m'"],
1631
+ [/size\s*=\s*\{\s*"md"\s*\}/g, 'size={"m"}'],
1632
+ [/size\s*=\s*\{\s*'md'\s*\}/g, "size={'m'}"]
1633
+ ];
1634
+ function rewrite(source) {
1635
+ let next = source;
1636
+ let hits = 0;
1637
+ for (const [re, repl] of PATTERNS) {
1638
+ next = next.replace(re, () => {
1639
+ hits++;
1640
+ return repl;
1641
+ });
1642
+ }
1643
+ return { next, hits };
1644
+ }
1645
+ async function migrateSizeSml(cwd, dry) {
1646
+ const changes = [];
1647
+ const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r));
1648
+ for (const file of files) {
1649
+ if (file.endsWith(".css"))
1650
+ continue;
1651
+ const raw = readFileSync9(file, "utf8");
1652
+ const { next, hits } = rewrite(raw);
1653
+ if (hits === 0)
1654
+ continue;
1655
+ if (writeIfChanged(file, next, dry) || dry) {
1656
+ changes.push({
1657
+ file: rel(cwd, file),
1658
+ detail: `${hits}\xD7 size sm/md \u2192 s/m`
1659
+ });
1660
+ }
1661
+ }
1662
+ return changes;
1663
+ }
1664
+
1665
+ // src/lib/migrate/field-border.ts
1666
+ import { readFileSync as readFileSync10 } from "fs";
1667
+ var VARS = [
1668
+ "--field-border-focus",
1669
+ "--field-border-hover",
1670
+ "--field-border-error",
1671
+ "--field-border-disabled"
1672
+ ];
1673
+ function extractColor(value) {
1674
+ const v = value.trim().replace(/;$/, "");
1675
+ const m = v.match(/^(?:\d+(?:\.\d+)?(?:px|em|rem)?\s+)?(?:solid|dashed|dotted|double)\s+(.+)$/i);
1676
+ if (m)
1677
+ return m[1].trim();
1678
+ if (/^(#|rgb|hsl|var\(|color-mix|transparent|currentColor)/i.test(v)) {
1679
+ return null;
1680
+ }
1681
+ return null;
1682
+ }
1683
+ function rewriteCss(source) {
1684
+ let hits = 0;
1685
+ let next = source;
1686
+ for (const name of VARS) {
1687
+ const re = new RegExp(`(${name.replace(/-/g, "\\-")}\\s*:\\s*)([^;\\n}]+)`, "g");
1688
+ next = next.replace(re, (full, prefix, rawVal) => {
1689
+ const color = extractColor(rawVal);
1690
+ if (!color)
1691
+ return full;
1692
+ hits++;
1693
+ return `${prefix}${color}`;
1694
+ });
1695
+ }
1696
+ return { next, hits };
1697
+ }
1698
+ async function migrateFieldBorder(cwd, dry) {
1699
+ const changes = [];
1700
+ const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r)).filter((f) => f.endsWith(".css"));
1701
+ for (const file of files) {
1702
+ const raw = readFileSync10(file, "utf8");
1703
+ const { next, hits } = rewriteCss(raw);
1704
+ if (hits === 0)
1705
+ continue;
1706
+ if (writeIfChanged(file, next, dry) || dry) {
1707
+ changes.push({
1708
+ file: rel(cwd, file),
1709
+ detail: `${hits}\xD7 border hover/focus \u2192 \u0442\u043E\u043B\u044C\u043A\u043E \u0446\u0432\u0435\u0442`
1710
+ });
1711
+ }
1712
+ }
1713
+ return changes;
1714
+ }
1715
+
1716
+ // src/lib/migrate/modal-create.ts
1717
+ import { readFileSync as readFileSync11 } from "fs";
1718
+ async function migrateModalCreate(cwd, dry) {
1719
+ const changes = [];
1720
+ const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r)).filter((f) => /\.(tsx|jsx)$/.test(f));
1721
+ const addRe = /Modal\.Add\s*\(\s*<([A-Za-z_][\w.]*)\s+key=\{Date\.now\(\)\}/;
1722
+ for (const file of files) {
1723
+ const raw = readFileSync11(file, "utf8");
1724
+ if (!addRe.test(raw) && !/Modal\.Add\s*\(/.test(raw))
1725
+ continue;
1726
+ if (/export\s+default\s+Modal\.Create\s*\(/.test(raw)) {
1727
+ if (/Modal\.Add\s*\(/.test(raw)) {
1728
+ changes.push({
1729
+ file: rel(cwd, file),
1730
+ detail: "\u043E\u0441\u0442\u0430\u043B\u0441\u044F Modal.Add \u0440\u044F\u0434\u043E\u043C \u0441 Create \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 Add \u0432\u0440\u0443\u0447\u043D\u0443\u044E"
1731
+ });
1732
+ }
1733
+ continue;
1734
+ }
1735
+ const simple = /function\s+Screen\b/.test(raw) || /const\s+Screen\s*=/.test(raw) || /function\s+\w+\s*\([^)]*\)\s*\{[\s\S]*Modal\.Main/.test(raw);
1736
+ const openWrapper = /(?:const|function)\s+Open\s*=\s*(?:\([^)]*\)|[A-Za-z_]\w*)\s*=>\s*\{?\s*Modal\.Add/;
1737
+ if (simple && openWrapper.test(raw) && /export\s+default/.test(raw)) {
1738
+ let next = raw;
1739
+ next = next.replace(/\n(?:const|function)\s+Open\s*=\s*[\s\S]*?(?=\n(?:const|function|export)\s)/, `
1740
+ `);
1741
+ next = next.replace(/\n(?:const|function)\s+Icon\s*=\s*[\s\S]*?(?=\n(?:const|function|export)\s)/, `
1742
+ `);
1743
+ next = next.replace(/\nconst\s+Add\s*=\s*\{[\s\S]*?\};\s*\nexport\s+default\s+Add\s*;?/, `
1744
+ export default Modal.Create(Screen);
1745
+ `);
1746
+ next = next.replace(/export\s+default\s+\{\s*Open\s*,?\s*Icon?\s*,?\s*\}\s*;?/, "export default Modal.Create(Screen);");
1747
+ next = next.replace(/export\s+function\s+open\w+\s*\([^)]*\)\s*\{[\s\S]*?Modal\.Add\([\s\S]*?\);\s*\}/, (block) => {
1748
+ return `/* TODO: \u0437\u0430\u043C\u0435\u043D\u0438\u0442\u0435 \u0432\u044B\u0437\u043E\u0432\u044B \u043D\u0430 Modal.Open("Module","Name", props) \u0438\u043B\u0438 Create.Open */
1749
+ ${block}`;
1750
+ });
1751
+ if (next !== raw && !/Modal\.Add/.test(next)) {
1752
+ if (writeIfChanged(file, next, dry) || dry) {
1753
+ changes.push({
1754
+ file: rel(cwd, file),
1755
+ detail: "Modal.Add \u2192 Modal.Create(Screen)"
1756
+ });
1757
+ }
1758
+ continue;
1759
+ }
1760
+ }
1761
+ const count = (raw.match(/Modal\.Add\s*\(/g) || []).length;
1762
+ changes.push({
1763
+ file: rel(cwd, file),
1764
+ detail: `${count}\xD7 Modal.Add \u2014 \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0438\u0442\u0435 \u043D\u0430 Modal.Create + Open (\u0441\u043C. Projects/modal/Create)`
1765
+ });
1766
+ }
1767
+ return changes;
1768
+ }
1769
+
1770
+ // src/lib/migrate/legacy-hacks.ts
1771
+ import { existsSync as existsSync7, readFileSync as readFileSync12 } from "fs";
1772
+ import { join as join7 } from "path";
1773
+ var HACK_RE = /\(globalThis\s+as\s+[^)]+\)\.React\s*=\s*React\s*;?|globalThis\.React\s*=\s*React\s*;?/;
1774
+ async function migrateLegacyReactHack(cwd, dry) {
1775
+ const changes = [];
1776
+ const files = new Set(projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r)).filter((f) => /(^|\/)(main|bootstrap)\.(tsx?|jsx?)$/.test(f)));
1777
+ for (const name of ["main.tsx", "main.ts", "src/main.tsx", "src/main.ts"]) {
1778
+ const full = join7(cwd, name);
1779
+ if (existsSync7(full))
1780
+ files.add(full);
1781
+ }
1782
+ for (const file of files) {
1783
+ const raw = readFileSync12(file, "utf8");
1784
+ if (!HACK_RE.test(raw))
1785
+ continue;
1786
+ const next = raw.replace(/\/\*\s*@elcrm\/modal[\s\S]*?\*\/\s*/m, "").replace(/\(globalThis\s+as\s+typeof\s+globalThis\s+&\s+\{\s*React:\s*typeof\s+React\s*\}\)\.React\s*=\s*React\s*;\s*/g, "").replace(/globalThis\.React\s*=\s*React\s*;\s*/g, "");
1787
+ if (next === raw) {
1788
+ changes.push({
1789
+ file: rel(cwd, file),
1790
+ detail: "globalThis.React \u2014 \u0443\u0434\u0430\u043B\u0438\u0442\u0435 \u0432\u0440\u0443\u0447\u043D\u0443\u044E \u043F\u043E\u0441\u043B\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F @elcrm/modal"
1791
+ });
1792
+ continue;
1793
+ }
1794
+ if (writeIfChanged(file, next, dry) || dry) {
1795
+ changes.push({
1796
+ file: rel(cwd, file),
1797
+ detail: "\u0443\u0434\u0430\u043B\u0451\u043D globalThis.React \u0445\u0430\u043A"
1798
+ });
1799
+ }
1800
+ }
1801
+ return changes;
1802
+ }
1803
+
1804
+ // src/lib/migrate/form-aliases.ts
1805
+ import { readFileSync as readFileSync13 } from "fs";
1806
+ var REPLACEMENTS = [
1807
+ [/\bProgressField\b/g, "RangeField", "ProgressField\u2192RangeField"],
1808
+ [/\bReadonlyField\b/g, "DisplayField", "ReadonlyField\u2192DisplayField"],
1809
+ [/\bOtpField\b/g, "CodeField", "OtpField\u2192CodeField"],
1810
+ [/\bSegmentField\b/g, "TabsField", "SegmentField\u2192TabsField"],
1811
+ [/\bDragonDropField\b/g, "DragDropField", "DragonDropField\u2192DragDropField"],
1812
+ [/\bTProgress\b/g, "TRange", "TProgress\u2192TRange"]
1813
+ ];
1814
+ function rewrite2(source) {
1815
+ let next = source;
1816
+ const labels = [];
1817
+ for (const [re, repl, label] of REPLACEMENTS) {
1818
+ let n = 0;
1819
+ next = next.replace(re, () => {
1820
+ n++;
1821
+ return repl;
1822
+ });
1823
+ if (n > 0)
1824
+ labels.push(`${n}\xD7 ${label}`);
1825
+ }
1826
+ return { next, labels };
1827
+ }
1828
+ async function migrateFormAliases(cwd, dry) {
1829
+ const changes = [];
1830
+ const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r));
1831
+ for (const file of files) {
1832
+ if (file.endsWith(".css"))
1833
+ continue;
1834
+ const raw = readFileSync13(file, "utf8");
1835
+ const { next, labels } = rewrite2(raw);
1836
+ if (labels.length === 0)
1837
+ continue;
1838
+ if (writeIfChanged(file, next, dry) || dry) {
1839
+ changes.push({
1840
+ file: rel(cwd, file),
1841
+ detail: labels.join(", ")
1842
+ });
1843
+ }
1844
+ }
1845
+ return changes;
1846
+ }
1847
+
1848
+ // src/lib/migrate/form-native.ts
1849
+ import { readFileSync as readFileSync14 } from "fs";
1850
+ function lineLooksLikeFormNativeProp(line) {
1851
+ const t = line.trim();
1852
+ if (!t || t.startsWith("//") || t.startsWith("*") || t.startsWith("/*")) {
1853
+ return false;
1854
+ }
1855
+ if (/\bnavigator\b|\bnativeEvent\b|\bcreateNative\b/.test(line)) {
1856
+ return false;
1857
+ }
1858
+ if (/['"`]native['"`]/.test(line))
1859
+ return false;
1860
+ if (/\binstallMode\b/.test(line) && /\bnative\b/.test(line))
1861
+ return false;
1862
+ return /(?:^|\s)native(?:\s*$|\s*=\s*\{?\s*true\s*\}?|(?=\s+[A-Za-z_./{])|(?=\s*\/?>))/.test(line);
1863
+ }
1864
+ function stripFormNativePropLine(line) {
1865
+ if (!lineLooksLikeFormNativeProp(line))
1866
+ return line;
1867
+ const trimmed = line.trim();
1868
+ if (trimmed === "native" || trimmed === "native={true}") {
1869
+ return "";
1870
+ }
1871
+ let next = line.replace(/\s+native=\{true\}/g, "");
1872
+ next = next.replace(/(?<=[\s{(])native(?=\s*\/?>)/g, "");
1873
+ next = next.replace(/(?<=\s)native(?=\s+[A-Za-z_./{])/g, "");
1874
+ if (/^\s*$/.test(next))
1875
+ return "";
1876
+ return next;
1877
+ }
1878
+ function rewriteFormNative(source) {
1879
+ const lines = source.split(/\r?\n/);
1880
+ let hits = 0;
1881
+ const out = [];
1882
+ for (const line of lines) {
1883
+ if (!lineLooksLikeFormNativeProp(line)) {
1884
+ out.push(line);
1885
+ continue;
1886
+ }
1887
+ const next = stripFormNativePropLine(line);
1888
+ if (next !== line)
1889
+ hits++;
1890
+ if (next === "" && line.trim() !== "") {
1891
+ continue;
1892
+ }
1893
+ out.push(next);
1894
+ }
1895
+ return { next: out.join(`
1896
+ `), hits };
1897
+ }
1898
+ async function migrateFormNative(cwd, dry) {
1899
+ const changes = [];
1900
+ const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r)).filter((f) => /\.(tsx|jsx)$/.test(f));
1901
+ for (const file of files) {
1902
+ const raw = readFileSync14(file, "utf8");
1903
+ const { next, hits } = rewriteFormNative(raw);
1904
+ if (hits === 0)
1905
+ continue;
1906
+ if (writeIfChanged(file, next, dry) || dry) {
1907
+ changes.push({
1908
+ file: rel(cwd, file),
1909
+ detail: `${hits}\xD7 \u0441\u043D\u044F\u0442 prop native`
1910
+ });
1911
+ }
1912
+ }
1913
+ return changes;
1914
+ }
1915
+
1916
+ // src/lib/migrate/native-html.ts
1917
+ import { readFileSync as readFileSync15 } from "fs";
1918
+ function findNativeHtmlTags(source) {
1919
+ const out = [];
1920
+ const re = /<(input|textarea|select)\b/gi;
1921
+ let m;
1922
+ while (m = re.exec(source)) {
1923
+ const name = m[1].toLowerCase();
1924
+ const start = m.index;
1925
+ let i = m.index + m[0].length;
1926
+ let quote = null;
1927
+ let brace = 0;
1928
+ let attrsEnd = -1;
1929
+ let selfClosing = false;
1930
+ while (i < source.length) {
1931
+ const ch = source[i];
1932
+ if (quote) {
1933
+ if (ch === "\\" && quote === '"') {
1934
+ i += 2;
1935
+ continue;
1936
+ }
1937
+ if (ch === quote)
1938
+ quote = null;
1939
+ i++;
1940
+ continue;
1941
+ }
1942
+ if (ch === '"' || ch === "'") {
1943
+ quote = ch;
1944
+ i++;
1945
+ continue;
1946
+ }
1947
+ if (ch === "{") {
1948
+ brace++;
1949
+ i++;
1950
+ continue;
1951
+ }
1952
+ if (ch === "}" && brace > 0) {
1953
+ brace--;
1954
+ i++;
1955
+ continue;
1956
+ }
1957
+ if (brace > 0) {
1958
+ i++;
1959
+ continue;
1960
+ }
1961
+ if (ch === "/" && source[i + 1] === ">") {
1962
+ attrsEnd = i;
1963
+ selfClosing = true;
1964
+ i += 2;
1965
+ out.push({
1966
+ start,
1967
+ end: i,
1968
+ name,
1969
+ attrs: source.slice(m.index + m[0].length, attrsEnd),
1970
+ inner: "",
1971
+ selfClosing: true
1972
+ });
1973
+ break;
1974
+ }
1975
+ if (ch === ">") {
1976
+ attrsEnd = i;
1977
+ i++;
1978
+ if (name === "input" || selfClosing) {
1979
+ out.push({
1980
+ start,
1981
+ end: i,
1982
+ name,
1983
+ attrs: source.slice(m.index + m[0].length, attrsEnd),
1984
+ inner: "",
1985
+ selfClosing: false
1986
+ });
1987
+ break;
1988
+ }
1989
+ const close = new RegExp(`</${name}\\s*>`, "i");
1990
+ close.lastIndex = i;
1991
+ const cm = close.exec(source);
1992
+ if (!cm)
1993
+ break;
1994
+ out.push({
1995
+ start,
1996
+ end: cm.index + cm[0].length,
1997
+ name,
1998
+ attrs: source.slice(m.index + m[0].length, attrsEnd),
1999
+ inner: source.slice(i, cm.index),
2000
+ selfClosing: false
2001
+ });
2002
+ i = cm.index + cm[0].length;
2003
+ break;
2004
+ }
2005
+ i++;
2006
+ }
2007
+ re.lastIndex = Math.max(re.lastIndex, i);
2008
+ }
2009
+ return out;
2010
+ }
2011
+ function attrType(attrs) {
2012
+ const m = /\btype\s*=\s*(?:\{["'](\w+)["']\}|["'](\w+)["'])/i.exec(attrs);
2013
+ return (m?.[1] || m?.[2] || "text").toLowerCase();
2014
+ }
2015
+ function hasAttr(attrs, name) {
2016
+ return new RegExp(`\\b${name}\\s*=`).test(attrs) || new RegExp(`(?:^|\\s)${name}(?:\\s|$)`).test(attrs);
2017
+ }
2018
+ function sliceAttrValue(attrs, eqIndex) {
2019
+ let i = eqIndex + 1;
2020
+ while (i < attrs.length && /\s/.test(attrs[i]))
2021
+ i++;
2022
+ if (i >= attrs.length)
2023
+ return null;
2024
+ const start = i;
2025
+ const ch = attrs[i];
2026
+ if (ch === '"' || ch === "'") {
2027
+ const q = ch;
2028
+ i++;
2029
+ while (i < attrs.length && attrs[i] !== q) {
2030
+ if (attrs[i] === "\\" && q === '"')
2031
+ i++;
2032
+ i++;
2033
+ }
2034
+ return { value: attrs.slice(start, i + 1), end: i + 1 };
2035
+ }
2036
+ if (ch === "{") {
2037
+ let brace = 0;
2038
+ let quote = null;
2039
+ while (i < attrs.length) {
2040
+ const c2 = attrs[i];
2041
+ if (quote) {
2042
+ if (c2 === "\\" && quote === '"') {
2043
+ i += 2;
2044
+ continue;
2045
+ }
2046
+ if (c2 === quote)
2047
+ quote = null;
2048
+ i++;
2049
+ continue;
2050
+ }
2051
+ if (c2 === '"' || c2 === "'") {
2052
+ quote = c2;
2053
+ i++;
2054
+ continue;
2055
+ }
2056
+ if (c2 === "{")
2057
+ brace++;
2058
+ else if (c2 === "}") {
2059
+ brace--;
2060
+ if (brace === 0) {
2061
+ i++;
2062
+ return { value: attrs.slice(start, i), end: i };
2063
+ }
2064
+ }
2065
+ i++;
2066
+ }
2067
+ return null;
2068
+ }
2069
+ while (i < attrs.length && /[^\s/>]/.test(attrs[i]))
2070
+ i++;
2071
+ return { value: attrs.slice(start, i), end: i };
2072
+ }
2073
+ function findAttrSpan(attrs, name) {
2074
+ const re = new RegExp(`(?:^|\\s)(${name})(?=\\s|=|$)`, "i");
2075
+ const m = re.exec(attrs);
2076
+ if (!m)
2077
+ return null;
2078
+ const nameStart = m.index + (m[0].startsWith(" ") || m[0].startsWith(`
2079
+ `) || m[0].startsWith("\t") ? 1 : 0);
2080
+ const nameIdx = attrs.indexOf(m[1], m.index);
2081
+ let i = nameIdx + m[1].length;
2082
+ while (i < attrs.length && /\s/.test(attrs[i]))
2083
+ i++;
2084
+ if (attrs[i] === "=") {
2085
+ const sliced = sliceAttrValue(attrs, i);
2086
+ if (!sliced)
2087
+ return null;
2088
+ return { start: m.index, end: sliced.end, value: sliced.value };
2089
+ }
2090
+ return { start: m.index, end: nameIdx + m[1].length, value: null };
2091
+ }
2092
+ function getAttrExpr(attrs, name) {
2093
+ const span = findAttrSpan(attrs, name);
2094
+ return span?.value ?? null;
2095
+ }
2096
+ function dropAttrs(attrs, names) {
2097
+ let next = attrs;
2098
+ for (const name of names) {
2099
+ for (;; ) {
2100
+ const span = findAttrSpan(next, name);
2101
+ if (!span)
2102
+ break;
2103
+ next = next.slice(0, span.start) + next.slice(span.end);
2104
+ }
2105
+ }
2106
+ return next;
2107
+ }
2108
+ function compactAttrs(attrs) {
2109
+ const t = attrs.replace(/\s+/g, " ").trim();
2110
+ return t ? ` ${t}` : "";
2111
+ }
2112
+ function rewriteOnChangeToOnValue(attrs, mode) {
2113
+ const onChange = getAttrExpr(attrs, "onChange");
2114
+ if (!onChange)
2115
+ return dropAttrs(attrs, ["onChange", "checked", "type"]);
2116
+ let body = onChange;
2117
+ if (mode === "value") {
2118
+ body = onChange.replace(/\(e\)\s*=>\s*/g, "({ value }) => ").replace(/\be\s*=>\s*/g, "({ value }) => ").replace(/e\.target\.value/g, "String(value ?? '')");
2119
+ } else if (mode === "checked") {
2120
+ body = onChange.replace(/\(e\)\s*=>\s*/g, "({ value }) => ").replace(/\be\s*=>\s*/g, "({ value }) => ").replace(/e\.target\.checked/g, "Boolean(value)");
2121
+ } else {
2122
+ body = onChange.replace(/\(e\)\s*=>\s*/g, "({ value }) => ").replace(/\be\s*=>\s*/g, "({ value }) => ").replace(/e\.target\.files/g, "(value as FileList | null)").replace(/e\.target\.files\??\.\[0\]/g, "(Array.isArray(value) ? value[0] : value)");
2123
+ }
2124
+ let next = dropAttrs(attrs, ["onChange", "type"]);
2125
+ if (mode === "checked") {
2126
+ const checked = getAttrExpr(attrs, "checked");
2127
+ next = dropAttrs(next, ["checked"]);
2128
+ if (checked)
2129
+ next += ` value=${checked}`;
2130
+ }
2131
+ next += ` onValue=${body}`;
2132
+ return next;
2133
+ }
2134
+ function convertInput(attrs) {
2135
+ const type = attrType(attrs);
2136
+ if (type === "hidden")
2137
+ return null;
2138
+ if (hasAttr(attrs, "ref"))
2139
+ return null;
2140
+ if (type === "checkbox") {
2141
+ if (!hasAttr(attrs, "checked") && !hasAttr(attrs, "onChange"))
2142
+ return null;
2143
+ const next = compactAttrs(dropAttrs(rewriteOnChangeToOnValue(attrs, "checked"), ["defaultChecked"]));
2144
+ return {
2145
+ jsx: `<CheckField variant="plain"${next} />`,
2146
+ imports: ["CheckField"]
2147
+ };
2148
+ }
2149
+ if (type === "file") {
2150
+ const next = compactAttrs(dropAttrs(rewriteOnChangeToOnValue(attrs, "files"), ["multiple"]));
2151
+ const multiple = hasAttr(attrs, "multiple") ? " multiple" : "";
2152
+ return {
2153
+ jsx: `<FileField keepFile${multiple}${next} />`,
2154
+ imports: ["FileField"]
2155
+ };
2156
+ }
2157
+ if (type === "text" || type === "search" || type === "tel" || type === "url" || type === "number" || type === "email" || type === "password") {
2158
+ const field = type === "password" ? "PasswordField" : type === "email" ? "EmailField" : type === "number" ? "NumberField" : "StringField";
2159
+ let next = rewriteOnChangeToOnValue(attrs, "value");
2160
+ next = dropAttrs(next, [
2161
+ "autoFocus",
2162
+ "autofocus",
2163
+ "inputMode",
2164
+ "inputmode",
2165
+ "onKeyDown",
2166
+ "onKeyUp",
2167
+ "onKeyPress"
2168
+ ]);
2169
+ const maxLength = getAttrExpr(attrs, "maxLength");
2170
+ if (maxLength && !getAttrExpr(next, "maxLength")) {
2171
+ next += ` maxLength=${maxLength}`;
2172
+ }
2173
+ return {
2174
+ jsx: `<${field}${compactAttrs(next)} />`,
2175
+ imports: [field]
2176
+ };
2177
+ }
2178
+ return null;
2179
+ }
2180
+ function convertTextarea(attrs) {
2181
+ if (hasAttr(attrs, "ref") || hasAttr(attrs, "onKeyDown"))
2182
+ return null;
2183
+ const next = compactAttrs(rewriteOnChangeToOnValue(attrs, "value"));
2184
+ return {
2185
+ jsx: `<TextareaField${next} />`,
2186
+ imports: ["TextareaField"]
2187
+ };
2188
+ }
2189
+ function ensureFormImports(source, names) {
2190
+ const uniq = [...new Set(names)];
2191
+ if (uniq.length === 0)
2192
+ return source;
2193
+ const importRe = /import\s*\{([^}]*)\}\s*from\s*["']@elcrm\/form["']\s*;?/;
2194
+ const m = importRe.exec(source);
2195
+ if (m) {
2196
+ const existing = m[1].split(",").map((s) => s.trim()).filter(Boolean);
2197
+ const set = new Set(existing);
2198
+ for (const n of uniq)
2199
+ set.add(n);
2200
+ const list = [...set].sort().join(", ");
2201
+ const after = source.slice(m.index + m[0].length);
2202
+ const needsNl = after.length > 0 && !/^\r?\n/.test(after);
2203
+ const stmt = `import { ${list} } from "@elcrm/form";${needsNl ? `
2204
+ ` : ""}`;
2205
+ return source.slice(0, m.index) + stmt + after;
2206
+ }
2207
+ const insert = `import { ${uniq.sort().join(", ")} } from "@elcrm/form";
2208
+ `;
2209
+ const reactImport = /import\s+.+\s+from\s+["']react["']\s*;?\n/;
2210
+ if (reactImport.test(source)) {
2211
+ return source.replace(reactImport, (block) => block + insert);
2212
+ }
2213
+ return insert + source;
2214
+ }
2215
+ function rewriteNativeHtml(source) {
2216
+ const tags = findNativeHtmlTags(source);
2217
+ if (tags.length === 0) {
2218
+ return { next: source, hits: 0, skipped: [] };
2219
+ }
2220
+ let hits = 0;
2221
+ const imports = [];
2222
+ const skipped = [];
2223
+ let next = source;
2224
+ for (let ti = tags.length - 1;ti >= 0; ti--) {
2225
+ const tag = tags[ti];
2226
+ let conv = null;
2227
+ if (tag.name === "input")
2228
+ conv = convertInput(tag.attrs);
2229
+ else if (tag.name === "textarea") {
2230
+ if (tag.inner.trim()) {
2231
+ skipped.push("textarea with children");
2232
+ continue;
2233
+ }
2234
+ conv = convertTextarea(tag.attrs);
2235
+ } else {
2236
+ skipped.push("select \u2192 SelectField \u0432\u0440\u0443\u0447\u043D\u0443\u044E (options id:number)");
2237
+ continue;
2238
+ }
2239
+ if (!conv) {
2240
+ skipped.push(`${tag.name} type=${attrType(tag.attrs)}`);
2241
+ continue;
2242
+ }
2243
+ next = next.slice(0, tag.start) + conv.jsx + next.slice(tag.end);
2244
+ imports.push(...conv.imports);
2245
+ hits++;
2246
+ }
2247
+ if (hits > 0)
2248
+ next = ensureFormImports(next, imports);
2249
+ return { next, hits, skipped };
2250
+ }
2251
+ async function migrateNativeHtml(cwd, dry) {
2252
+ const changes = [];
2253
+ const pkg = readPackageJson(cwd);
2254
+ if (pkg?.name === "@elcrm/form")
2255
+ return changes;
2256
+ const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r)).filter((f) => /\.(tsx|jsx)$/.test(f));
2257
+ for (const file of files) {
2258
+ const raw = readFileSync15(file, "utf8");
2259
+ if (!/<(?:input|textarea|select)\b/.test(raw))
2260
+ continue;
2261
+ const { next, hits, skipped } = rewriteNativeHtml(raw);
2262
+ const detailParts = [];
2263
+ if (hits > 0)
2264
+ detailParts.push(`${hits}\xD7 \u2192 @elcrm/form`);
2265
+ if (skipped.length > 0) {
2266
+ detailParts.push(`\u043F\u0440\u043E\u043F\u0443\u0441\u043A: ${[...new Set(skipped)].slice(0, 3).join("; ")}`);
2267
+ }
2268
+ if (detailParts.length === 0)
2269
+ continue;
2270
+ if (hits > 0) {
2271
+ if (writeIfChanged(file, next, dry) || dry) {
2272
+ changes.push({
2273
+ file: rel(cwd, file),
2274
+ detail: detailParts.join("; ")
2275
+ });
2276
+ }
2277
+ } else {
2278
+ changes.push({
2279
+ file: rel(cwd, file),
2280
+ detail: detailParts.join("; ")
2281
+ });
2282
+ }
2283
+ }
2284
+ return changes;
2285
+ }
2286
+
2287
+ // src/lib/migrate/front.ts
2288
+ var FRONT_MIGRATIONS = {
2289
+ "size-sml": migrateSizeSml,
2290
+ "field-border": migrateFieldBorder,
2291
+ "modal-create": migrateModalCreate,
2292
+ "form-aliases": migrateFormAliases,
2293
+ "form-tokens": migrateFormTokens,
2294
+ "form-native": migrateFormNative,
2295
+ "native-html": migrateNativeHtml,
2296
+ components: migrateComponents,
2297
+ "css-sanitize": migrateCssSanitize,
2298
+ "legacy-hacks": migrateLegacyReactHack,
2299
+ "socket-server": migrateSocketServer
2300
+ };
2301
+ var FRONT_MIGRATE_ORDER = [
2302
+ "form-aliases",
2303
+ "form-native",
2304
+ "native-html",
2305
+ "form-tokens",
2306
+ "components",
2307
+ "css-sanitize",
2308
+ "size-sml",
2309
+ "field-border",
2310
+ "modal-create",
2311
+ "socket-server"
2312
+ ];
2313
+ async function migrateFrontAll(cwd, dry) {
2314
+ const all = [];
2315
+ for (const name of FRONT_MIGRATE_ORDER) {
2316
+ const part = await FRONT_MIGRATIONS[name](cwd, dry);
2317
+ for (const c2 of part) {
2318
+ all.push({ file: c2.file, detail: `[${name}] ${c2.detail}` });
1820
2319
  }
1821
2320
  }
1822
- return { styleDir, added, imports, removedImports, skippedPkgs };
2321
+ return all;
1823
2322
  }
1824
2323
 
1825
- // src/commands/test.ts
1826
- import { resolve } from "path";
1827
-
1828
- // src/lib/test/detect.ts
1829
- import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync13, statSync as statSync4 } from "fs";
1830
- import { join as join7 } from "path";
1831
- function hasElcrmServer(pkg) {
1832
- for (const section of [
1833
- "dependencies",
1834
- "devDependencies",
1835
- "peerDependencies"
1836
- ]) {
1837
- const map = pkg[section];
1838
- if (map && typeof map === "object" && "@elcrm/server" in map)
1839
- return true;
2324
+ // src/lib/tidy.ts
2325
+ function applyPackageHygiene(pkg) {
2326
+ const done = [];
2327
+ if (pkg.type !== "module") {
2328
+ pkg.type = "module";
2329
+ done.push('type: "module"');
1840
2330
  }
1841
- return false;
1842
- }
1843
- function detectTestMode(pkg, force) {
1844
- if (force === "server" || force === "front")
1845
- return force;
1846
- return hasElcrmServer(pkg) ? "server" : "front";
1847
- }
1848
- function listWorkspaceDirs(cwd, pkg) {
1849
- const raw = pkg.workspaces;
1850
- let patterns = [];
1851
- if (Array.isArray(raw)) {
1852
- patterns = raw.filter((p) => typeof p === "string");
1853
- } else if (raw && typeof raw === "object" && Array.isArray(raw.packages)) {
1854
- patterns = raw.packages.filter((p) => typeof p === "string");
2331
+ if (!pkg.engines)
2332
+ pkg.engines = {};
2333
+ if (!pkg.engines.node && !pkg.engines.bun) {
2334
+ pkg.engines.node = ">=18";
2335
+ done.push("engines.node >=18");
1855
2336
  }
1856
- if (!patterns.length)
1857
- return [];
1858
- const out = [];
1859
- for (const pat of patterns) {
1860
- if (!pat.includes("*")) {
1861
- const dir = join7(cwd, pat);
1862
- if (existsSync7(join7(dir, "package.json")))
1863
- out.push(dir);
1864
- continue;
2337
+ if (pkg.name?.startsWith("@elcrm/")) {
2338
+ if (!pkg.scripts)
2339
+ pkg.scripts = {};
2340
+ if (!pkg.scripts["pack:check"]) {
2341
+ pkg.scripts["pack:check"] = "npm pack --dry-run";
2342
+ done.push("scripts.pack:check");
1865
2343
  }
1866
- const base = pat.replace(/\/\*$/, "").replace(/\*$/, "");
1867
- const parent = join7(cwd, base);
1868
- if (!existsSync7(parent) || !statSync4(parent).isDirectory())
2344
+ }
2345
+ return done;
2346
+ }
2347
+ function printCssSync(css) {
2348
+ let added = 0;
2349
+ for (const [file, tokens] of Object.entries(css.added)) {
2350
+ if (!tokens.length)
1869
2351
  continue;
1870
- for (const name of readdirSync4(parent)) {
1871
- const dir = join7(parent, name);
1872
- if (statSync4(dir).isDirectory() && existsSync7(join7(dir, "package.json"))) {
1873
- out.push(dir);
1874
- }
1875
- }
2352
+ added += tokens.length;
2353
+ console.log(` ${c.green("+")} ${file}: ${tokens.join(", ")}`);
2354
+ }
2355
+ if (css.imports.length) {
2356
+ console.log(` ${c.green("+")} elcrm.css @import: ${css.imports.join(", ")}`);
2357
+ }
2358
+ if (css.removedImports.length) {
2359
+ console.log(` ${c.red("\u2212")} elcrm.css \u0431\u0438\u0442\u044B\u0435 @import: ${css.removedImports.join(", ")}`);
2360
+ }
2361
+ if (added === 0 && css.imports.length === 0 && css.removedImports.length === 0) {
2362
+ console.log(" \u0422\u043E\u043A\u0435\u043D\u044B \u0438 @import \u0443\u0436\u0435 \u043D\u0430 \u043C\u0435\u0441\u0442\u0435.");
1876
2363
  }
1877
- return out;
1878
2364
  }
1879
- function readPkgAt(dir) {
1880
- const path = join7(dir, "package.json");
1881
- if (!existsSync7(path))
1882
- return null;
1883
- try {
1884
- return JSON.parse(readFileSync13(path, "utf8"));
1885
- } catch {
1886
- return null;
2365
+ function printChanges(changes, empty) {
2366
+ if (!changes.length) {
2367
+ console.log(` ${empty}`);
2368
+ return;
2369
+ }
2370
+ for (const ch of changes)
2371
+ console.log(` ${ch.file}: ${ch.detail}`);
2372
+ console.log(` \u0418\u0442\u043E\u0433\u043E: ${changes.length}.`);
2373
+ }
2374
+ async function tidyFrontProject(opts) {
2375
+ const { cwd, pkg, dry } = opts;
2376
+ const prefix = dry ? "[dry] " : "";
2377
+ console.log(`
2378
+ ${prefix}1) package.json`);
2379
+ const hygiene = applyPackageHygiene(pkg);
2380
+ if (hygiene.length) {
2381
+ if (!dry)
2382
+ writePackageJson(pkg, cwd);
2383
+ console.log(` ${hygiene.join(", ")}`);
2384
+ } else {
2385
+ console.log(" \u0431\u0435\u0437 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439.");
2386
+ }
2387
+ console.log(`
2388
+ ${prefix}2) migrate front (\u0430\u043B\u0438\u0430\u0441\u044B, native, \u0442\u043E\u043A\u0435\u043D\u044B, size, Modal.Create, \u2026)`);
2389
+ const migrate = await migrateFrontAll(cwd, dry);
2390
+ printChanges(migrate, "\u043A\u043E\u0434\u0430 \u043C\u0435\u043D\u044F\u0442\u044C \u043D\u0435 \u043D\u0443\u0436\u043D\u043E.");
2391
+ let hacks = [];
2392
+ if (opts.hacks) {
2393
+ console.log(`
2394
+ ${prefix}2b) migrate legacy-hacks`);
2395
+ hacks = await migrateLegacyReactHack(cwd, dry);
2396
+ printChanges(hacks, "\u0445\u0430\u043A\u043E\u0432 \u043D\u0435\u0442.");
2397
+ }
2398
+ console.log(`
2399
+ ${prefix}3) CSS: \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u044E\u0449\u0438\u0435 --field-* / \u0442\u043E\u043A\u0435\u043D\u044B \u043F\u0430\u043A\u0435\u0442\u043E\u0432, \u2212\u0431\u0438\u0442\u044B\u0435 @import`);
2400
+ let css = null;
2401
+ let cssError = null;
2402
+ const names = uniqueElcrmNamesInTree(cwd, pkg);
2403
+ if (!names.length) {
2404
+ console.log(" \u043D\u0435\u0442 @elcrm/* \u2014 sync CSS \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D.");
2405
+ } else {
2406
+ try {
2407
+ css = syncElcrmCss({
2408
+ cwd,
2409
+ pkgNames: names,
2410
+ dry
2411
+ });
2412
+ printCssSync(css);
2413
+ } catch (e) {
2414
+ cssError = e instanceof Error ? e.message : String(e);
2415
+ console.log(` ${c.gray(cssError)}`);
2416
+ }
1887
2417
  }
2418
+ console.log(`
2419
+ ${prefix}4) css-sanitize (\u043E\u0441\u0438\u0440\u043E\u0442\u0435\u0432\u0448\u0438\u0439 color-mix)`);
2420
+ const sanitize = await migrateCssSanitize(cwd, dry);
2421
+ printChanges(sanitize, "\u0431\u0435\u0437 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439.");
2422
+ return { hygiene, migrate, css, cssError, sanitize, hacks };
1888
2423
  }
1889
2424
 
2425
+ // src/commands/test.ts
2426
+ import { resolve } from "path";
2427
+
1890
2428
  // src/lib/test/front.ts
1891
- import { existsSync as existsSync8, readdirSync as readdirSync5, readFileSync as readFileSync14, statSync as statSync5 } from "fs";
2429
+ import { existsSync as existsSync8, readdirSync as readdirSync5, readFileSync as readFileSync16, statSync as statSync5 } from "fs";
1892
2430
  import { join as join8, relative as relative2 } from "path";
1893
- init_form_tokens();
1894
- init_components();
1895
- init_css_sanitize();
1896
2431
  var SKIP = new Set(["node_modules", "dist", ".git", "coverage", ".bun"]);
1897
2432
  function isThemeTokenFile(rel2) {
1898
2433
  const n = rel2.replace(/\\/g, "/");
@@ -1909,6 +2444,9 @@ function lineForColorScan(line) {
1909
2444
  return line.replace(/var\([^)]*\)/g, "").replace(/url\s*\([^)]*\)/gi, "");
1910
2445
  }
1911
2446
  function hardcodedColorReason(line) {
2447
+ const trimmed = line.trim();
2448
+ if (/^--[\w-]+\s*:/.test(trimmed))
2449
+ return null;
1912
2450
  const s = lineForColorScan(line);
1913
2451
  if (HARD_HEX_RE.test(s) || HARD_RGB_RE.test(s) || JSX_COLOR_PROP_RE.test(line)) {
1914
2452
  return "\u043B\u0438\u0442\u0435\u0440\u0430\u043B \u0446\u0432\u0435\u0442\u0430 \u2014 var(--button-*|--field-*|--item-*|--layout-*) \u0438\u0437 theme";
@@ -2027,6 +2565,7 @@ function collectFrontIssues(cwd, pkg) {
2027
2565
  }
2028
2566
  const files = [];
2029
2567
  walkCode(join8(cwd, "src"), files);
2568
+ walkCode(join8(cwd, "web", "src"), files);
2030
2569
  walkCode(join8(cwd, "style"), files);
2031
2570
  for (const name of ["bootstrap.tsx", "bootstrap.ts", "main.tsx", "main.ts", "index.tsx", "index.ts", "App.tsx"]) {
2032
2571
  const full = join8(cwd, name);
@@ -2037,7 +2576,7 @@ function collectFrontIssues(cwd, pkg) {
2037
2576
  files.push(inSrc);
2038
2577
  }
2039
2578
  for (const file of files) {
2040
- const text = readFileSync14(file, "utf8");
2579
+ const text = readFileSync16(file, "utf8");
2041
2580
  const rel2 = relative2(cwd, file);
2042
2581
  const skipHardColor = isLib || isThemeTokenFile(rel2) || isGeneratedIcons(rel2);
2043
2582
  if (DEPRECATED_SOCKET_IMPORT_RE.test(text)) {
@@ -2078,32 +2617,35 @@ function collectFrontIssues(cwd, pkg) {
2078
2617
  });
2079
2618
  }
2080
2619
  const lines = text.split(/\r?\n/);
2620
+ let prevNonEmpty = "";
2081
2621
  lines.forEach((line, i) => {
2082
2622
  const trimmed = line.trim();
2083
2623
  if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) {
2084
2624
  return;
2085
2625
  }
2086
2626
  if (/<(?:input|textarea|select)\b/.test(line)) {
2087
- if (pkg.name === "@elcrm/form")
2088
- return;
2089
- issues.push({
2090
- level: "error",
2091
- code: "native-html-input",
2092
- message: "\u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 <input|textarea|select> \u2014 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 @elcrm/form (StringField, Select, \u2026)",
2093
- file: rel2,
2094
- line: i + 1
2095
- });
2627
+ if (pkg.name !== "@elcrm/form") {
2628
+ issues.push({
2629
+ level: "error",
2630
+ code: "native-html-input",
2631
+ message: "\u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 <input|textarea|select> \u2014 @elcrm/form (elcrm migrate native-html)",
2632
+ file: rel2,
2633
+ line: i + 1,
2634
+ fixable: true
2635
+ });
2636
+ }
2096
2637
  }
2097
- if (/(?:^|\s)native(?:\s|$|=|\/|>)/.test(line) && !/\bnavigator\b|\bnativeEvent\b|\bcreateNative/.test(line)) {
2098
- if (pkg.name === "@elcrm/form")
2099
- return;
2100
- issues.push({
2101
- level: "warn",
2102
- code: "form-native-prop",
2103
- message: "prop native \u0443 \u043F\u043E\u043B\u044F \u0444\u043E\u0440\u043C\u044B \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435; \u0441\u0442\u0438\u043B\u0438 \u0447\u0435\u0440\u0435\u0437 \u0442\u043E\u043A\u0435\u043D\u044B @elcrm/form",
2104
- file: rel2,
2105
- line: i + 1
2106
- });
2638
+ if (/(?:^|\s)native(?:\s|$|=|\/|>)/.test(line) && !/\bnavigator\b|\bnativeEvent\b|\bcreateNative/.test(line) && !/['"`]native['"`]/.test(line) && !(/\binstallMode\b/.test(line) && /\bnative\b/.test(line))) {
2639
+ if (pkg.name !== "@elcrm/form") {
2640
+ issues.push({
2641
+ level: "warn",
2642
+ code: "form-native-prop",
2643
+ message: "prop native \u0443 \u043F\u043E\u043B\u044F \u0444\u043E\u0440\u043C\u044B \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 (elcrm migrate form-native)",
2644
+ file: rel2,
2645
+ line: i + 1,
2646
+ fixable: true
2647
+ });
2648
+ }
2107
2649
  }
2108
2650
  if (/size\s*=\s*["'](?:sm|md)["']/.test(line) && !/titleSize/.test(line)) {
2109
2651
  issues.push({
@@ -2155,7 +2697,7 @@ function collectFrontIssues(cwd, pkg) {
2155
2697
  fixable: true
2156
2698
  });
2157
2699
  }
2158
- if (lineHasOrphanColorMix(line)) {
2700
+ if (lineHasOrphanColorMix(line, prevNonEmpty)) {
2159
2701
  issues.push({
2160
2702
  level: "error",
2161
2703
  code: "css-orphan-mix",
@@ -2165,6 +2707,8 @@ function collectFrontIssues(cwd, pkg) {
2165
2707
  fixable: true
2166
2708
  });
2167
2709
  }
2710
+ if (trimmed.length > 0)
2711
+ prevNonEmpty = line;
2168
2712
  if (/\b(Block|Row|Column)\b/.test(line) && /@elcrm\/components/.test(text) && /from\s+["']@elcrm\/components/.test(text)) {
2169
2713
  if (/import\s*\{[^}]*\b(Block|Row|Column)\b/.test(line) || /<\/?(?:Block|Row|Column)\b/.test(line)) {
2170
2714
  issues.push({
@@ -2204,7 +2748,7 @@ function collectFrontIssues(cwd, pkg) {
2204
2748
  return issues;
2205
2749
  }
2206
2750
  async function runPkgScript(cwd, script) {
2207
- const pkg = JSON.parse(readFileSync14(join8(cwd, "package.json"), "utf8"));
2751
+ const pkg = JSON.parse(readFileSync16(join8(cwd, "package.json"), "utf8"));
2208
2752
  if (!pkg.scripts?.[script])
2209
2753
  return { ran: false, ok: true };
2210
2754
  console.log(`\u2192 bun run ${script}`);
@@ -2247,7 +2791,7 @@ function printIssues(issues, title) {
2247
2791
  }
2248
2792
 
2249
2793
  // src/lib/test/serverScan.ts
2250
- import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync15, statSync as statSync6 } from "fs";
2794
+ import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync17, statSync as statSync6 } from "fs";
2251
2795
  import { join as join9, relative as relative3 } from "path";
2252
2796
 
2253
2797
  // src/lib/test/serverRules.ts
@@ -2649,7 +3193,7 @@ function scanServerProject(root) {
2649
3193
  walkFiles2(root, root, files);
2650
3194
  const findings = [];
2651
3195
  for (const file of files) {
2652
- const text = readFileSync15(file, "utf8");
3196
+ const text = readFileSync17(file, "utf8");
2653
3197
  if (file.endsWith("package.json")) {
2654
3198
  findings.push(...scanPackageJson(root, file, text));
2655
3199
  } else {
@@ -2697,7 +3241,7 @@ elcrm test (server) \u2014 ${root}`);
2697
3241
  }
2698
3242
 
2699
3243
  // src/commands/test.ts
2700
- async function testOneDir(cwd, force, runScripts) {
3244
+ async function testOneDir(cwd, force, runScripts, fix, dry) {
2701
3245
  const pkg = readPkgAt(cwd);
2702
3246
  if (!pkg) {
2703
3247
  console.error(`package.json \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${cwd}`);
@@ -2711,14 +3255,32 @@ elcrm test \u2014 ${cwd}
2711
3255
  const result = scanServerProject(cwd);
2712
3256
  return printServerFindings(result);
2713
3257
  }
3258
+ if (fix) {
3259
+ console.log(`
3260
+ ${c.cyan("\u2192")} migrate front${dry ? " (--dry)" : ""}\u2026`);
3261
+ const changes = await migrateFrontAll(cwd, dry);
3262
+ if (!changes.length) {
3263
+ console.log("\u0410\u0432\u0442\u043E\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0439 \u043D\u0435 \u043F\u043E\u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043B\u043E\u0441\u044C.");
3264
+ } else {
3265
+ for (const ch of changes) {
3266
+ console.log(` ${ch.file}: ${ch.detail}`);
3267
+ }
3268
+ console.log(dry ? `
3269
+ --dry: ${changes.length} \u043F\u043E\u0442\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u044B\u0445 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439.` : `
3270
+ \u041F\u0440\u0438\u043C\u0435\u043D\u0435\u043D\u043E: ${changes.length}.`);
3271
+ }
3272
+ if (dry) {
3273
+ console.log(c.gray("(--dry: \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u0430\u043D\u0430\u043B\u0438\u0437 \u043F\u043E \u0438\u0441\u0445\u043E\u0434\u043D\u043E\u043C\u0443 \u043A\u043E\u0434\u0443)"));
3274
+ }
3275
+ }
2714
3276
  const issues = collectFrontIssues(cwd, pkg);
2715
3277
  let errors = printIssues(issues, `
2716
3278
  \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u0430\u043D\u0430\u043B\u0438\u0437:`);
2717
- if (runScripts) {
3279
+ if (runScripts && !dry) {
2718
3280
  console.log("");
2719
3281
  const scriptFails = await runFrontScripts(cwd);
2720
3282
  errors += scriptFails;
2721
- } else {
3283
+ } else if (!runScripts) {
2722
3284
  console.log(c.gray(`
2723
3285
  (--no-scripts: check:css / check:tokens / test \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D\u044B)`));
2724
3286
  }
@@ -2728,6 +3290,8 @@ async function cmdTest(parsed) {
2728
3290
  const target = resolve(parsed.positionals[0] ?? process.cwd());
2729
3291
  const force = hasFlag(parsed, "server") ? "server" : hasFlag(parsed, "front") ? "front" : null;
2730
3292
  const runScripts = !hasFlag(parsed, "no-scripts");
3293
+ const fix = hasFlag(parsed, "fix");
3294
+ const dry = hasFlag(parsed, "dry");
2731
3295
  const rootPkg = readPkgAt(target);
2732
3296
  if (!rootPkg) {
2733
3297
  console.error("package.json \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.");
@@ -2739,17 +3303,20 @@ async function cmdTest(parsed) {
2739
3303
  console.log(c.dim(`workspaces: ${workspaces.map((d) => d.replace(target + "/", "")).join(", ")}`));
2740
3304
  const rootHasElcrm = Boolean(rootPkg.dependencies && Object.keys(rootPkg.dependencies).some((k) => k.startsWith("@elcrm/"))) || Boolean(rootPkg.devDependencies && Object.keys(rootPkg.devDependencies).some((k) => k.startsWith("@elcrm/")));
2741
3305
  if (rootHasElcrm || hasFlag(parsed, "root")) {
2742
- totalErrors += await testOneDir(target, force, runScripts);
3306
+ totalErrors += await testOneDir(target, force, runScripts, fix, dry);
2743
3307
  }
2744
3308
  for (const dir of workspaces) {
2745
- totalErrors += await testOneDir(dir, force, runScripts);
3309
+ totalErrors += await testOneDir(dir, force, runScripts, fix, dry);
2746
3310
  }
2747
3311
  } else {
2748
- totalErrors += await testOneDir(target, force, runScripts);
3312
+ totalErrors += await testOneDir(target, force, runScripts, fix, dry);
2749
3313
  }
2750
3314
  if (totalErrors > 0) {
2751
3315
  console.log(c.yellow(`
2752
3316
  elcrm test: ${totalErrors} error(s)`));
3317
+ if (!fix) {
3318
+ console.log(c.dim("\u0418\u0441\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435: elcrm test --fix / elcrm migrate front"));
3319
+ }
2753
3320
  process.exit(1);
2754
3321
  }
2755
3322
  console.log(c.green(`
@@ -2827,29 +3394,8 @@ async function cmdUpdate(parsed) {
2827
3394
  --dry: \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E (\u043A \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044E: ${changed}).`);
2828
3395
  if (doFix) {
2829
3396
  console.log(`
2830
- --fix + --dry: \u043F\u0440\u0435\u0432\u044C\u044E migrate front\u2026`);
2831
- const front = await migrateFrontAll(cwd, true);
2832
- if (front.length === 0)
2833
- console.log(" \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0439 \u043A\u043E\u0434\u0430 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F.");
2834
- else
2835
- for (const c2 of front)
2836
- console.log(` ${c2.file}: ${c2.detail}`);
2837
- console.log(`
2838
- --fix + --dry: \u043F\u0440\u0435\u0432\u044C\u044E CSS-\u0442\u043E\u043A\u0435\u043D\u043E\u0432\u2026`);
2839
- try {
2840
- const css = syncElcrmCss({ cwd, pkgNames: names, dry: true });
2841
- let n = 0;
2842
- for (const [file, tokens] of Object.entries(css.added)) {
2843
- if (!tokens.length)
2844
- continue;
2845
- n += tokens.length;
2846
- console.log(` ${file}: ${tokens.join(", ")}`);
2847
- }
2848
- if (n === 0)
2849
- console.log(" \u041D\u043E\u0432\u044B\u0445 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u043D\u0435\u0442.");
2850
- } catch (e) {
2851
- console.log(` ${c.gray("css:")} ${e instanceof Error ? e.message : e}`);
2852
- }
3397
+ --fix + --dry: \u043F\u0440\u0435\u0432\u044C\u044E \u043D\u0430\u0432\u0435\u0434\u0435\u043D\u0438\u044F \u043F\u043E\u0440\u044F\u0434\u043A\u0430 (\u043A\u0430\u043A elcrm doctor)\u2026`);
3398
+ await tidyFrontProject({ cwd, pkg, dry: true });
2853
3399
  }
2854
3400
  return;
2855
3401
  }
@@ -2872,46 +3418,9 @@ package.json \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D (${changed}).`);
2872
3418
  }
2873
3419
  if (doFix) {
2874
3420
  console.log(`
2875
- --fix: migrate front (form-aliases, size, \u2026)\u2026`);
2876
- const front = await migrateFrontAll(cwd, false);
2877
- if (front.length === 0) {
2878
- console.log(" \u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u043A\u043E\u0434\u0430 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F.");
2879
- } else {
2880
- for (const ch of front)
2881
- console.log(` ${ch.file}: ${ch.detail}`);
2882
- console.log(` \u041F\u0440\u0438\u043C\u0435\u043D\u0435\u043D\u043E: ${front.length}.`);
2883
- }
2884
- console.log(`
2885
- --fix: sync CSS-\u0442\u043E\u043A\u0435\u043D\u043E\u0432 (elcrm css)\u2026`);
2886
- try {
2887
- const pkgAfter = readPackageJson(cwd) ?? pkg;
2888
- const cssNames = uniqueElcrmNames(listElcrmDeps(pkgAfter));
2889
- const css = syncElcrmCss({ cwd, pkgNames: cssNames, dry: false });
2890
- let added = 0;
2891
- for (const [file, tokens] of Object.entries(css.added)) {
2892
- if (!tokens.length)
2893
- continue;
2894
- added += tokens.length;
2895
- console.log(` ${c.green("+")} ${file}: ${tokens.join(", ")}`);
2896
- }
2897
- if (css.imports.length) {
2898
- console.log(` ${c.green("+")} elcrm.css @import: ${css.imports.join(", ")}`);
2899
- }
2900
- if (css.removedImports.length) {
2901
- console.log(` ${c.red("\u2212")} elcrm.css \u0431\u0438\u0442\u044B\u0435 @import: ${css.removedImports.join(", ")}`);
2902
- }
2903
- if (added === 0 && css.imports.length === 0 && css.removedImports.length === 0) {
2904
- console.log(" \u0422\u043E\u043A\u0435\u043D\u044B \u0443\u0436\u0435 \u043D\u0430 \u043C\u0435\u0441\u0442\u0435.");
2905
- }
2906
- const cleaned = await migrateCssSanitize(cwd, false);
2907
- if (cleaned.length) {
2908
- console.log(" css-sanitize:");
2909
- for (const ch of cleaned)
2910
- console.log(` ${ch.file}: ${ch.detail}`);
2911
- }
2912
- } catch (e) {
2913
- console.log(` ${c.gray("css sync \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D:")} ${e instanceof Error ? e.message : e}`);
2914
- }
3421
+ --fix: \u043D\u0430\u0432\u0435\u0434\u0435\u043D\u0438\u0435 \u043F\u043E\u0440\u044F\u0434\u043A\u0430 (elcrm doctor)\u2026`);
3422
+ const pkgAfter = readPackageJson(cwd) ?? pkg;
3423
+ await tidyFrontProject({ cwd, pkg: pkgAfter, dry: false });
2915
3424
  }
2916
3425
  if (doTest) {
2917
3426
  console.log(`
@@ -2933,7 +3442,7 @@ import {
2933
3442
  existsSync as existsSync11,
2934
3443
  mkdirSync,
2935
3444
  readdirSync as readdirSync7,
2936
- readFileSync as readFileSync16,
3445
+ readFileSync as readFileSync18,
2937
3446
  statSync as statSync7,
2938
3447
  writeFileSync as writeFileSync5
2939
3448
  } from "fs";
@@ -2977,7 +3486,7 @@ function isEmptyDir(dir) {
2977
3486
  function writePkgName(pkgPath, name) {
2978
3487
  if (!existsSync11(pkgPath))
2979
3488
  return;
2980
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
3489
+ const pkg = JSON.parse(readFileSync18(pkgPath, "utf8"));
2981
3490
  pkg.name = name;
2982
3491
  pkg.version = pkg.version ?? "0.0.0";
2983
3492
  writeFileSync5(pkgPath, JSON.stringify(pkg, null, 2) + `
@@ -3031,6 +3540,16 @@ function libPackageName(pathOrName) {
3031
3540
  function doneHint(template, cdPath) {
3032
3541
  const prefix = cdPath ? ` cd ${cdPath}
3033
3542
  ` : "";
3543
+ if (template === "orbit") {
3544
+ console.log(`
3545
+ \u0413\u043E\u0442\u043E\u0432\u043E (orbit: \u0432\u0438\u0442\u0440\u0438\u043D\u0430 \u043D\u0430 @elcrm/components).
3546
+
3547
+ ${prefix} bun run dev
3548
+
3549
+ Web \u2192 http://localhost:5173
3550
+ `);
3551
+ return;
3552
+ }
3034
3553
  if (template === "panel") {
3035
3554
  console.log(`
3036
3555
  \u0413\u043E\u0442\u043E\u0432\u043E (panel: web + api).
@@ -3099,8 +3618,6 @@ async function cmdInit(parsed) {
3099
3618
  }
3100
3619
 
3101
3620
  // src/commands/migrate.ts
3102
- init_socket_server();
3103
- init_front();
3104
3621
  var MIGRATIONS = {
3105
3622
  "socket-server": migrateSocketServer,
3106
3623
  ...FRONT_MIGRATIONS,
@@ -3138,101 +3655,255 @@ async function cmdMigrate(parsed) {
3138
3655
  console.log("\u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F.");
3139
3656
  return;
3140
3657
  }
3141
- for (const c2 of changes) {
3142
- console.log(` ${c2.file}: ${c2.detail}`);
3658
+ for (const c2 of changes) {
3659
+ console.log(` ${c2.file}: ${c2.detail}`);
3660
+ }
3661
+ console.log(dry ? `
3662
+ --dry: ${changes.length} \u043F\u043E\u0442\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u044B\u0445 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439.` : `
3663
+ \u041F\u0440\u0438\u043C\u0435\u043D\u0435\u043D\u043E: ${changes.length}.`);
3664
+ if (name === "front" || name === "ui" || name === "size-sml" || name === "form-tokens" || name === "form-native" || name === "native-html" || name === "components" || name === "css-sanitize") {
3665
+ console.log("\u0414\u0430\u043B\u044C\u0448\u0435: elcrm css && elcrm audit");
3666
+ }
3667
+ }
3668
+
3669
+ // src/commands/doctor.ts
3670
+ import { resolve as resolve5 } from "path";
3671
+
3672
+ // src/commands/docs.ts
3673
+ import { join as join13, resolve as resolve3 } from "path";
3674
+
3675
+ // src/lib/projectKind.ts
3676
+ function detectProjectKind(cwd) {
3677
+ const pkg = readPkgAt(cwd);
3678
+ if (!pkg)
3679
+ return "front";
3680
+ const workspaces = listWorkspaceDirs(cwd, pkg);
3681
+ if (workspaces.length) {
3682
+ const modes = new Set;
3683
+ for (const dir of workspaces) {
3684
+ const w = readPkgAt(dir);
3685
+ if (w)
3686
+ modes.add(detectTestMode(w));
3687
+ }
3688
+ if (modes.has("front") && modes.has("server"))
3689
+ return "mixed";
3690
+ if (modes.has("server") && !modes.has("front"))
3691
+ return "server";
3692
+ if (modes.has("front"))
3693
+ return "front";
3694
+ }
3695
+ return hasElcrmServer(pkg) ? "server" : "front";
3696
+ }
3697
+ function isElcrmLibPackage(cwd) {
3698
+ const pkg = readPkgAt(cwd);
3699
+ const name = pkg?.name;
3700
+ return typeof name === "string" && name.startsWith("@elcrm/");
3701
+ }
3702
+
3703
+ // src/lib/elcrmDocsCatalog.ts
3704
+ var ALL_ELCRM_DOCS = [
3705
+ "CLI.elCRM.md",
3706
+ "API.elCRM.md",
3707
+ "ALERT.elCRM.md",
3708
+ "BUTTON.elCRM.md",
3709
+ "MODAL.elCRM.md",
3710
+ "NOTICE.elCRM.md",
3711
+ "NOTIFICATIONS.elCRM.md",
3712
+ "PASSKEY.elCRM.md",
3713
+ "ROUTER.elCRM.md",
3714
+ "SOCKET.elCRM.md",
3715
+ "TOOLTIP.elCRM.md",
3716
+ "ICONS.elCRM.md",
3717
+ "FORM.elCRM.md",
3718
+ "COMPONENTS.elCRM.md",
3719
+ "SERVER.elCRM.md"
3720
+ ];
3721
+ var SERVER_DOCS = [
3722
+ "SERVER.elCRM.md",
3723
+ "CLI.elCRM.md",
3724
+ "API.elCRM.md",
3725
+ "SOCKET.elCRM.md"
3726
+ ];
3727
+ function docsForProject(kind, lib) {
3728
+ if (lib || kind === "front" || kind === "mixed")
3729
+ return ALL_ELCRM_DOCS;
3730
+ return SERVER_DOCS;
3731
+ }
3732
+
3733
+ // src/lib/scaffoldFiles.ts
3734
+ import {
3735
+ existsSync as existsSync12,
3736
+ mkdirSync as mkdirSync2,
3737
+ readdirSync as readdirSync8,
3738
+ readFileSync as readFileSync19,
3739
+ writeFileSync as writeFileSync6
3740
+ } from "fs";
3741
+ import { dirname as dirname3, join as join12 } from "path";
3742
+ function writeScaffoldFile(dest, body, opts) {
3743
+ if (opts.dry)
3744
+ return "dry";
3745
+ if (!opts.force && existsSync12(dest))
3746
+ return "skip";
3747
+ mkdirSync2(dirname3(dest), { recursive: true });
3748
+ writeFileSync6(dest, body, "utf8");
3749
+ return "write";
3750
+ }
3751
+ function readBundled(relPath) {
3752
+ const full = join12(packageRoot(), "templates", relPath);
3753
+ if (!existsSync12(full)) {
3754
+ throw new Error(`\u0428\u0430\u0431\u043B\u043E\u043D \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${relPath}`);
3755
+ }
3756
+ return readFileSync19(full, "utf8");
3757
+ }
3758
+
3759
+ // src/commands/docs.ts
3760
+ async function cmdDocs(parsed) {
3761
+ const cwd = resolve3(parsed.options.dir || process.cwd());
3762
+ const dry = hasFlag(parsed, "dry");
3763
+ const force = hasFlag(parsed, "force") || !hasFlag(parsed, "keep");
3764
+ const lib = hasFlag(parsed, "lib") || isElcrmLibPackage(cwd);
3765
+ const kind = detectProjectKind(cwd);
3766
+ const names = docsForProject(kind, lib);
3767
+ console.log(`${dry ? "[dry] " : ""}elcrm docs \u2192 ${cwd}/docs (${kind}${lib ? ", lib" : ""})`);
3768
+ for (const name of names) {
3769
+ const body = readBundled(join13("elcrm-docs", name));
3770
+ const dest = join13(cwd, "docs", name);
3771
+ const action = writeScaffoldFile(dest, body, { dry, force });
3772
+ const mark = action === "write" ? c.green("+") : action === "skip" ? c.gray("=") : c.cyan("~");
3773
+ console.log(` ${mark} docs/${name}`);
3774
+ }
3775
+ if (kind === "server" && !lib) {
3776
+ console.log(c.gray("\u0441\u0435\u0440\u0432\u0435\u0440: SERVER, CLI, API, SOCKET"));
3143
3777
  }
3144
- console.log(dry ? `
3145
- --dry: ${changes.length} \u043F\u043E\u0442\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u044B\u0445 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439.` : `
3146
- \u041F\u0440\u0438\u043C\u0435\u043D\u0435\u043D\u043E: ${changes.length}.`);
3147
- if (name === "front" || name === "ui" || name === "size-sml" || name === "form-tokens" || name === "components" || name === "css-sanitize") {
3148
- console.log("\u0414\u0430\u043B\u044C\u0448\u0435: elcrm css && elcrm audit");
3778
+ console.log("\u0414\u0430\u043B\u044C\u0448\u0435: elcrm cursor \u2014 \u043F\u0440\u0430\u0432\u0438\u043B\u0430 \u0432 .cursor/rules (+ .cursor/docs)");
3779
+ }
3780
+
3781
+ // src/commands/cursor.ts
3782
+ import { join as join14, resolve as resolve4 } from "path";
3783
+ var ALWAYS = ["elcrm.mdc", "elcrm-packages.mdc", "elcrm-jsdoc.mdc"];
3784
+ var UI = [
3785
+ "elcrm-ui.mdc",
3786
+ "elcrm-module.mdc",
3787
+ "elcrm-theme.mdc",
3788
+ "elcrm-tokens.mdc"
3789
+ ];
3790
+ var SERVER = ["elcrm-server.mdc", "elcrm-rpc.mdc"];
3791
+ var LIB = ["elcrm-lib.mdc"];
3792
+ function writeOne(dest, body, dry, force, label) {
3793
+ const action = writeScaffoldFile(dest, body, { dry, force });
3794
+ const mark = action === "write" ? c.green("+") : action === "skip" ? c.gray("=") : c.cyan("~");
3795
+ console.log(` ${mark} ${label}`);
3796
+ }
3797
+ async function cmdCursor(parsed) {
3798
+ const cwd = resolve4(parsed.options.dir || process.cwd());
3799
+ const dry = hasFlag(parsed, "dry");
3800
+ const force = hasFlag(parsed, "force") || !hasFlag(parsed, "keep");
3801
+ const lib = hasFlag(parsed, "lib") || isElcrmLibPackage(cwd);
3802
+ const kind = detectProjectKind(cwd);
3803
+ const ruleNames = [
3804
+ ...ALWAYS,
3805
+ ...kind === "server" ? [] : UI,
3806
+ ...kind === "front" ? [] : SERVER,
3807
+ ...lib ? LIB : []
3808
+ ];
3809
+ console.log(`${dry ? "[dry] " : ""}elcrm cursor \u2192 ${cwd}/.cursor (${kind}${lib ? ", lib" : ""})`);
3810
+ for (const name of ruleNames) {
3811
+ let body = readBundled(join14("elcrm-cursor", "rules", name));
3812
+ if (kind === "server" && (name === "elcrm-server.mdc" || name === "elcrm-rpc.mdc")) {
3813
+ body = body.replace("alwaysApply: false", "alwaysApply: true").replace(/^globs:.*\n/m, "");
3814
+ }
3815
+ writeOne(join14(cwd, ".cursor", "rules", name), body, dry, force, `.cursor/rules/${name}`);
3816
+ }
3817
+ const docs = docsForProject(kind, lib);
3818
+ for (const name of docs) {
3819
+ const body = readBundled(join14("elcrm-docs", name));
3820
+ writeOne(join14(cwd, ".cursor", "docs", name), body, dry, force, `.cursor/docs/${name}`);
3149
3821
  }
3822
+ writeOne(join14(cwd, "AGENTS.md"), readBundled(join14("elcrm-cursor", "AGENTS.md")), dry, force, "AGENTS.md");
3823
+ console.log(c.gray("\u0410\u0433\u0435\u043D\u0442: AGENTS.md + rules (\u0443\u0437\u043A\u0438\u0435 glob) + .cursor/docs. \u0421\u0434\u0430\u0447\u0430: elcrm test."));
3150
3824
  }
3151
3825
 
3152
3826
  // src/commands/doctor.ts
3153
- init_socket_server();
3154
- init_pkg();
3155
3827
  async function cmdDoctor(parsed) {
3156
- const cwd = process.cwd();
3157
- const fix = hasFlag(parsed, "fix");
3828
+ const cwd = resolve5(parsed.options.dir || process.cwd());
3829
+ const dry = hasFlag(parsed, "dry");
3830
+ const doDocs = hasFlag(parsed, "docs");
3831
+ const doTest = hasFlag(parsed, "test");
3832
+ const hacks = hasFlag(parsed, "hacks");
3158
3833
  const pkg = readPackageJson(cwd);
3159
3834
  if (!pkg) {
3160
3835
  console.error("package.json \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.");
3161
3836
  process.exit(1);
3162
3837
  }
3838
+ console.log(`elcrm doctor \u2192 ${cwd}`);
3839
+ console.log(c.dim("\u0441\u043A\u0440\u0438\u043F\u0442\u044B: package.json \u2192 migrate front (\u0441\u0442\u0430\u0440\u043E\u0435\u2192\u043D\u043E\u0432\u043E\u0435, \u2212\u043D\u0435\u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u043C\u043E\u0435) \u2192 css (\u2212\u0431\u0438\u0442\u044B\u0435 @import, +\u0442\u043E\u043A\u0435\u043D\u044B)"));
3840
+ if (dry) {
3841
+ console.log(c.gray("\u043F\u0440\u0435\u0432\u044C\u044E (--dry), \u0444\u0430\u0439\u043B\u044B \u043D\u0435 \u043F\u0438\u0448\u0435\u043C."));
3842
+ }
3843
+ const workspaces = listWorkspaceDirs(cwd, pkg);
3844
+ let errors = 0;
3845
+ const rootUiDeps = listElcrmDeps(pkg).filter((d) => d.name !== "@elcrm/server");
3846
+ const scanRootFront = workspaces.length === 0 ? !hasElcrmServer(pkg) : rootUiDeps.length > 0;
3163
3847
  if (hasElcrmServer(pkg)) {
3164
- const result = scanServerProject(cwd);
3165
- const errors = printServerFindings(result);
3166
- if (!fix) {
3167
- if (result.findings.some((f) => f.auto)) {
3168
- console.log(`
3169
- \u0410\u0432\u0442\u043E\u0437\u0430\u043C\u0435\u043D\u044B: bunx elcrm-server update (\u0438\u043B\u0438 elcrm migrate \u2026)`);
3170
- }
3171
- if (errors)
3172
- process.exit(1);
3173
- return;
3174
- }
3175
- console.log("doctor --fix \u043D\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435: \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 bunx elcrm-server update");
3176
- if (errors)
3177
- process.exit(1);
3178
- return;
3848
+ console.log(`
3849
+ \u0441\u043A\u0430\u043D @elcrm/server:`);
3850
+ errors += printServerFindings(scanServerProject(cwd));
3179
3851
  }
3180
- const issues = collectFrontIssues(cwd, pkg);
3181
- if (issues.length === 0) {
3182
- console.log("doctor: \u0437\u0430\u043C\u0435\u0447\u0430\u043D\u0438\u0439 \u043D\u0435\u0442.");
3183
- return;
3852
+ if (scanRootFront) {
3853
+ console.log(`
3854
+ \u0441\u043A\u0430\u043D UI:`);
3855
+ errors += printIssues(collectFrontIssues(cwd, pkg));
3184
3856
  }
3185
- printIssues(issues);
3186
- if (!fix) {
3187
- const fixable = issues.filter((i) => i.fixable).length;
3188
- if (fixable) {
3857
+ for (const dir of workspaces) {
3858
+ const child = readPackageJson(dir);
3859
+ if (!child)
3860
+ continue;
3861
+ if (hasElcrmServer(child)) {
3862
+ console.log(`
3863
+ \u0441\u043A\u0430\u043D server ${dir}:`);
3864
+ errors += printServerFindings(scanServerProject(dir));
3865
+ } else {
3189
3866
  console.log(`
3190
- \u0418\u0441\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438: elcrm doctor --fix (${fixable})`);
3867
+ \u0441\u043A\u0430\u043D UI ${dir}:`);
3868
+ errors += printIssues(collectFrontIssues(dir, child));
3191
3869
  }
3192
- if (issues.some((i) => i.level === "error"))
3193
- process.exit(1);
3194
- return;
3195
- }
3196
- let pkgDirty = false;
3197
- if (pkg.type !== "module") {
3198
- pkg.type = "module";
3199
- pkgDirty = true;
3200
- }
3201
- if (!pkg.engines)
3202
- pkg.engines = {};
3203
- if (!pkg.engines.node && !pkg.engines.bun) {
3204
- pkg.engines.node = ">=18";
3205
- pkgDirty = true;
3206
3870
  }
3207
- const isLib = Boolean(pkg.name?.startsWith("@elcrm/"));
3208
- if (isLib) {
3209
- if (!pkg.scripts)
3210
- pkg.scripts = {};
3211
- if (!pkg.scripts["pack:check"]) {
3212
- pkg.scripts["pack:check"] = "npm pack --dry-run";
3213
- pkgDirty = true;
3214
- }
3871
+ const tidy = await tidyFrontProject({ cwd, pkg, dry, hacks });
3872
+ if (doDocs) {
3873
+ const flags = dry ? ["dry"] : [];
3874
+ const child = {
3875
+ command: "docs",
3876
+ positionals: [],
3877
+ options: { dir: cwd },
3878
+ flags
3879
+ };
3880
+ console.log(`
3881
+ ${dry ? "[dry] " : ""}docs + cursor`);
3882
+ await cmdDocs(child);
3883
+ await cmdCursor({ ...child, command: "cursor" });
3215
3884
  }
3216
- if (pkgDirty)
3217
- writePackageJson(pkg, cwd);
3218
- const mig = await migrateSocketServer(cwd, false);
3219
- if (mig.length) {
3220
- console.log(`\u041C\u0438\u0433\u0440\u0430\u0446\u0438\u044F socket-server: ${mig.length} \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439.`);
3885
+ const n = tidy.hygiene.length + tidy.migrate.length + tidy.sanitize.length + tidy.hacks.length + (tidy.css ? Object.values(tidy.css.added).reduce((a, t) => a + t.length, 0) + tidy.css.imports.length + tidy.css.removedImports.length : 0);
3886
+ if (dry) {
3887
+ console.log(`
3888
+ \u043F\u0440\u0435\u0432\u044C\u044E: ${n} \u043F\u043E\u0442\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u044B\u0445 \u043F\u0440\u0430\u0432\u043E\u043A. \u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C: elcrm doctor`);
3889
+ } else {
3890
+ console.log(c.green(`
3891
+ doctor: \u0433\u043E\u0442\u043E\u0432\u043E (${n} \u043F\u0440\u0430\u0432\u043E\u043A).`));
3221
3892
  }
3222
- const { migrateFrontAll: migrateFrontAll2 } = await Promise.resolve().then(() => (init_front(), exports_front));
3223
- const front = await migrateFrontAll2(cwd, false);
3224
- if (front.length) {
3225
- console.log(`migrate front: ${front.length} \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439.`);
3893
+ if (doTest && !dry) {
3894
+ console.log(`
3895
+ --test: elcrm test\u2026`);
3896
+ await cmdTest(parseArgs(["test", cwd]));
3897
+ return;
3226
3898
  }
3227
- console.log("doctor --fix: \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u044B\u0435 \u043F\u0440\u0430\u0432\u043A\u0438 \u043F\u0440\u0438\u043C\u0435\u043D\u0435\u043D\u044B.");
3228
- console.log("\u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u0442\u0441\u044F: elcrm update --fix --test");
3899
+ if (errors && dry)
3900
+ process.exit(1);
3229
3901
  }
3230
3902
 
3231
3903
  // src/commands/build.ts
3232
- import { existsSync as existsSync12 } from "fs";
3904
+ import { existsSync as existsSync13 } from "fs";
3233
3905
 
3234
3906
  // src/lib/nextVersion.ts
3235
- init_pkg();
3236
3907
  async function nextVersion() {
3237
3908
  const pkg = readPackageJson();
3238
3909
  if (!pkg?.version) {
@@ -3253,9 +3924,9 @@ async function nextVersion() {
3253
3924
 
3254
3925
  // src/commands/build.ts
3255
3926
  function resolveEntrypoint() {
3256
- if (existsSync12("./src/index.js"))
3927
+ if (existsSync13("./src/index.js"))
3257
3928
  return "./src/index.js";
3258
- if (existsSync12("./src/index.ts"))
3929
+ if (existsSync13("./src/index.ts"))
3259
3930
  return "./src/index.ts";
3260
3931
  return "./src/index.js";
3261
3932
  }
@@ -3333,22 +4004,22 @@ async function cmdUuid() {
3333
4004
  }
3334
4005
 
3335
4006
  // src/commands/postbuild.ts
3336
- import { resolve as resolve3 } from "path";
4007
+ import { resolve as resolve6 } from "path";
3337
4008
 
3338
4009
  // src/vite/postbuild.ts
3339
4010
  import {
3340
- existsSync as existsSync13,
3341
- readdirSync as readdirSync9,
3342
- readFileSync as readFileSync18,
4011
+ existsSync as existsSync14,
4012
+ readdirSync as readdirSync10,
4013
+ readFileSync as readFileSync21,
3343
4014
  rmSync,
3344
4015
  statSync as statSync8,
3345
- writeFileSync as writeFileSync7
4016
+ writeFileSync as writeFileSync8
3346
4017
  } from "fs";
3347
- import { join as join13 } from "path";
4018
+ import { join as join16 } from "path";
3348
4019
 
3349
4020
  // src/vite/concat-css.ts
3350
- import { readdirSync as readdirSync8, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "fs";
3351
- import { join as join12 } from "path";
4021
+ import { readdirSync as readdirSync9, readFileSync as readFileSync20, writeFileSync as writeFileSync7 } from "fs";
4022
+ import { join as join15 } from "path";
3352
4023
  var SKIP_CSS = new Set([
3353
4024
  "index.css",
3354
4025
  "tokens.css",
@@ -3363,11 +4034,11 @@ function concatDistCss(options) {
3363
4034
  outFile,
3364
4035
  ...options.skip ?? []
3365
4036
  ]);
3366
- const files = readdirSync8(options.distDir).filter((f) => f.endsWith(".css") && !skip.has(f)).sort();
3367
- const css = files.map((f) => readFileSync17(join12(options.distDir, f), "utf8")).join(`
4037
+ const files = readdirSync9(options.distDir).filter((f) => f.endsWith(".css") && !skip.has(f)).sort();
4038
+ const css = files.map((f) => readFileSync20(join15(options.distDir, f), "utf8")).join(`
3368
4039
  `);
3369
- const outPath = join12(options.distDir, outFile);
3370
- writeFileSync6(outPath, css);
4040
+ const outPath = join15(options.distDir, outFile);
4041
+ writeFileSync7(outPath, css);
3371
4042
  console.log(`[concat-css] ${files.length} \u0444\u0430\u0439\u043B\u043E\u0432 \u2192 ${outFile}`);
3372
4043
  return outPath;
3373
4044
  }
@@ -3384,19 +4055,19 @@ function postbuildLib(options) {
3384
4055
  distDir: dist,
3385
4056
  outFile: options.cssOutFile ?? "index.css"
3386
4057
  });
3387
- for (const name of readdirSync9(dist)) {
3388
- const dir = join13(dist, name);
4058
+ for (const name of readdirSync10(dist)) {
4059
+ const dir = join16(dist, name);
3389
4060
  if (!statSync8(dir).isDirectory())
3390
4061
  continue;
3391
4062
  if (keep.has(name))
3392
4063
  continue;
3393
- const impl = join13(dir, `${name}.d.ts`);
3394
- const nestedIndex = join13(dir, "index.d.ts");
3395
- const flat = join13(dist, `${name}.d.ts`);
3396
- const src = existsSync13(impl) ? impl : existsSync13(nestedIndex) ? nestedIndex : null;
4064
+ const impl = join16(dir, `${name}.d.ts`);
4065
+ const nestedIndex = join16(dir, "index.d.ts");
4066
+ const flat = join16(dist, `${name}.d.ts`);
4067
+ const src = existsSync14(impl) ? impl : existsSync14(nestedIndex) ? nestedIndex : null;
3397
4068
  if (src) {
3398
- const text = readFileSync18(src, "utf8").replace(/from ['"]\.\.\/([^'"]+)['"]/g, `from './$1'`);
3399
- writeFileSync7(flat, text);
4069
+ const text = readFileSync21(src, "utf8").replace(/from ['"]\.\.\/([^'"]+)['"]/g, `from './$1'`);
4070
+ writeFileSync8(flat, text);
3400
4071
  console.log(`[postbuild] types \u2192 ${name}.d.ts`);
3401
4072
  }
3402
4073
  rmSync(dir, { recursive: true, force: true });
@@ -3407,14 +4078,12 @@ function postbuildLib(options) {
3407
4078
  // src/commands/postbuild.ts
3408
4079
  async function cmdPostbuild(parsed) {
3409
4080
  const dir = parsed.options.dir || "./dist";
3410
- const distDir = resolve3(process.cwd(), dir);
4081
+ const distDir = resolve6(process.cwd(), dir);
3411
4082
  postbuildLib({ distDir });
3412
4083
  console.log(`[postbuild] \u0433\u043E\u0442\u043E\u0432\u043E: ${distDir}`);
3413
4084
  }
3414
4085
 
3415
4086
  // src/commands/css.ts
3416
- init_pkg();
3417
- init_css_sanitize();
3418
4087
  async function cmdCss(parsed) {
3419
4088
  const cwd = parsed.options.dir || process.cwd();
3420
4089
  const pkg = readPackageJson(cwd);
@@ -3477,14 +4146,8 @@ css-sanitize: \u043F\u043E\u0447\u0438\u043D\u0435\u043D\u043E ${cleaned.length}
3477
4146
  }
3478
4147
 
3479
4148
  // src/commands/audit.ts
3480
- import { existsSync as existsSync14, readFileSync as readFileSync19 } from "fs";
3481
- import { join as join14, relative as relative4 } from "path";
3482
- init_pkg();
3483
- init_walk();
3484
- init_front();
3485
- init_form_tokens();
3486
- init_components();
3487
- init_css_sanitize();
4149
+ import { existsSync as existsSync15, readFileSync as readFileSync22 } from "fs";
4150
+ import { join as join17, relative as relative4 } from "path";
3488
4151
  function scan(cwd) {
3489
4152
  const out = [];
3490
4153
  const pkg = readPackageJson(cwd);
@@ -3515,7 +4178,7 @@ function scan(cwd) {
3515
4178
  }
3516
4179
  const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r));
3517
4180
  for (const file of files) {
3518
- const text = readFileSync19(file, "utf8");
4181
+ const text = readFileSync22(file, "utf8");
3519
4182
  const rel2 = relative4(cwd, file);
3520
4183
  const lines = text.split(/\r?\n/);
3521
4184
  lines.forEach((line, i) => {
@@ -3542,6 +4205,26 @@ function scan(cwd) {
3542
4205
  fix: "elcrm migrate modal-create"
3543
4206
  });
3544
4207
  }
4208
+ if (/<(?:input|textarea|select)\b/.test(line)) {
4209
+ out.push({
4210
+ level: "error",
4211
+ code: "native-html-input",
4212
+ message: "\u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 <input|textarea|select> \u2192 @elcrm/form",
4213
+ file: rel2,
4214
+ line: n,
4215
+ fix: "elcrm migrate native-html"
4216
+ });
4217
+ }
4218
+ if (/(?:^|\s)native(?:\s|$|=|\/|>)/.test(line) && !/\bnavigator\b|\bnativeEvent\b|\bcreateNative/.test(line) && !/['"`]native['"`]/.test(line) && !(/\binstallMode\b/.test(line) && /\bnative\b/.test(line))) {
4219
+ out.push({
4220
+ level: "warn",
4221
+ code: "form-native-prop",
4222
+ message: "prop native \u0443 \u043F\u043E\u043B\u044F \u0444\u043E\u0440\u043C\u044B \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435",
4223
+ file: rel2,
4224
+ line: n,
4225
+ fix: "elcrm migrate form-native"
4226
+ });
4227
+ }
3545
4228
  if (/key=\{Date\.now\(\)\}/.test(line) && /Modal/.test(text)) {
3546
4229
  out.push({
3547
4230
  level: "info",
@@ -3597,8 +4280,8 @@ function scan(cwd) {
3597
4280
  return false;
3598
4281
  if (/theme(-light|-dark)?\.css$/.test(f))
3599
4282
  return false;
3600
- const t = readFileSync19(f, "utf8");
3601
- return t.includes("--search-border:") && t.includes("--button-secondary-bg:") && t.includes("transparent");
4283
+ const t = readFileSync22(f, "utf8");
4284
+ return t.includes("--field-border:") && t.includes("--button-secondary-bg:") && t.includes("transparent");
3602
4285
  });
3603
4286
  if (toolbarMarkers.length > 1) {
3604
4287
  out.push({
@@ -3608,8 +4291,8 @@ function scan(cwd) {
3608
4291
  fix: "\u043E\u0431\u0449\u0438\u0439 \u043A\u043B\u0430\u0441\u0441 + theme tokens"
3609
4292
  });
3610
4293
  }
3611
- const styleDir = ["src/style", "src/styles", "style"].map((d) => join14(cwd, d)).find(existsSync14);
3612
- if (styleDir && !existsSync14(join14(styleDir, "elcrm.css"))) {
4294
+ const styleDir = ["src/style", "src/styles", "style"].map((d) => join17(cwd, d)).find(existsSync15);
4295
+ if (styleDir && !existsSync15(join17(styleDir, "elcrm.css"))) {
3613
4296
  out.push({
3614
4297
  level: "info",
3615
4298
  code: "elcrm-css",
@@ -3617,8 +4300,8 @@ function scan(cwd) {
3617
4300
  fix: "elcrm css"
3618
4301
  });
3619
4302
  }
3620
- if (styleDir && existsSync14(join14(styleDir, "elcrm.css"))) {
3621
- const elcrmCss = readFileSync19(join14(styleDir, "elcrm.css"), "utf8");
4303
+ if (styleDir && existsSync15(join17(styleDir, "elcrm.css"))) {
4304
+ const elcrmCss = readFileSync22(join17(styleDir, "elcrm.css"), "utf8");
3622
4305
  for (const m of elcrmCss.matchAll(/@import\s+["'](@elcrm\/[^"']+)["']/g)) {
3623
4306
  const spec = m[1];
3624
4307
  if (!resolveElcrmCssImport(spec, cwd)) {
@@ -3626,7 +4309,7 @@ function scan(cwd) {
3626
4309
  level: "error",
3627
4310
  code: "elcrm-import-missing",
3628
4311
  message: `\u0431\u0438\u0442\u044B\u0439 @import "${spec}" \u2014 \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442 \u0432 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u043E\u043C \u043F\u0430\u043A\u0435\u0442\u0435`,
3629
- file: relative4(cwd, join14(styleDir, "elcrm.css")),
4312
+ file: relative4(cwd, join17(styleDir, "elcrm.css")),
3630
4313
  fix: "elcrm css"
3631
4314
  });
3632
4315
  }
@@ -3638,8 +4321,8 @@ function scan(cwd) {
3638
4321
  "theme-light.css",
3639
4322
  "theme-dark.css",
3640
4323
  "theme.css"
3641
- ].map((f) => join14(styleDir, f));
3642
- const themeText = themeFiles.filter(existsSync14).map((f) => readFileSync19(f, "utf8")).join(`
4324
+ ].map((f) => join17(styleDir, f));
4325
+ const themeText = themeFiles.filter(existsSync15).map((f) => readFileSync22(f, "utf8")).join(`
3643
4326
  `);
3644
4327
  if (themeText && !/--popup-shadow\s*:/.test(themeText) && /--date-popup-shadow\s*:|--select-background\s*:/.test(themeText)) {
3645
4328
  out.push({
@@ -3661,12 +4344,16 @@ function scan(cwd) {
3661
4344
  for (const file of files) {
3662
4345
  if (!file.endsWith(".css"))
3663
4346
  continue;
3664
- const text = readFileSync19(file, "utf8");
4347
+ const text = readFileSync22(file, "utf8");
3665
4348
  const relPath = relative4(cwd, file);
3666
4349
  const lines = text.split(/\r?\n/);
4350
+ let prevNonEmpty = "";
3667
4351
  lines.forEach((line, i) => {
3668
- if (/:\s*var\(\s*--field-padding/.test(line))
4352
+ if (/:\s*var\(\s*--field-padding/.test(line)) {
4353
+ if (line.trim())
4354
+ prevNonEmpty = line;
3669
4355
  return;
4356
+ }
3670
4357
  if (lineHasDeprecatedFormToken(line)) {
3671
4358
  out.push({
3672
4359
  level: "warn",
@@ -3687,7 +4374,7 @@ function scan(cwd) {
3687
4374
  fix: "elcrm migrate components"
3688
4375
  });
3689
4376
  }
3690
- if (lineHasOrphanColorMix(line)) {
4377
+ if (lineHasOrphanColorMix(line, prevNonEmpty)) {
3691
4378
  out.push({
3692
4379
  level: "error",
3693
4380
  code: "css-orphan-mix",
@@ -3697,6 +4384,8 @@ function scan(cwd) {
3697
4384
  fix: "elcrm migrate css-sanitize"
3698
4385
  });
3699
4386
  }
4387
+ if (line.trim())
4388
+ prevNonEmpty = line;
3700
4389
  });
3701
4390
  }
3702
4391
  return out;
@@ -3744,160 +4433,6 @@ ${c.cyan("\u2192")} elcrm migrate front${hasFlag(parsed, "dry") ? " --dry" : ""}
3744
4433
  console.log("\u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u0442\u0441\u044F: elcrm css && elcrm doctor");
3745
4434
  }
3746
4435
 
3747
- // src/commands/docs.ts
3748
- import { join as join16, resolve as resolve4 } from "path";
3749
-
3750
- // src/lib/projectKind.ts
3751
- function detectProjectKind(cwd) {
3752
- const pkg = readPkgAt(cwd);
3753
- if (!pkg)
3754
- return "front";
3755
- const workspaces = listWorkspaceDirs(cwd, pkg);
3756
- if (workspaces.length) {
3757
- const modes = new Set;
3758
- for (const dir of workspaces) {
3759
- const w = readPkgAt(dir);
3760
- if (w)
3761
- modes.add(detectTestMode(w));
3762
- }
3763
- if (modes.has("front") && modes.has("server"))
3764
- return "mixed";
3765
- if (modes.has("server") && !modes.has("front"))
3766
- return "server";
3767
- if (modes.has("front"))
3768
- return "front";
3769
- }
3770
- return hasElcrmServer(pkg) ? "server" : "front";
3771
- }
3772
- function isElcrmLibPackage(cwd) {
3773
- const pkg = readPkgAt(cwd);
3774
- const name = pkg?.name;
3775
- return typeof name === "string" && name.startsWith("@elcrm/");
3776
- }
3777
-
3778
- // src/lib/elcrmDocsCatalog.ts
3779
- var ALL_ELCRM_DOCS = [
3780
- "CLI.elCRM.md",
3781
- "API.elCRM.md",
3782
- "ALERT.elCRM.md",
3783
- "BUTTON.elCRM.md",
3784
- "MODAL.elCRM.md",
3785
- "NOTICE.elCRM.md",
3786
- "NOTIFICATIONS.elCRM.md",
3787
- "PASSKEY.elCRM.md",
3788
- "ROUTER.elCRM.md",
3789
- "SOCKET.elCRM.md",
3790
- "TOOLTIP.elCRM.md",
3791
- "ICONS.elCRM.md",
3792
- "FORM.elCRM.md",
3793
- "COMPONENTS.elCRM.md",
3794
- "SERVER.elCRM.md"
3795
- ];
3796
- var SERVER_DOCS = [
3797
- "SERVER.elCRM.md",
3798
- "CLI.elCRM.md",
3799
- "API.elCRM.md",
3800
- "SOCKET.elCRM.md"
3801
- ];
3802
- function docsForProject(kind, lib) {
3803
- if (lib || kind === "front" || kind === "mixed")
3804
- return ALL_ELCRM_DOCS;
3805
- return SERVER_DOCS;
3806
- }
3807
-
3808
- // src/lib/scaffoldFiles.ts
3809
- import {
3810
- existsSync as existsSync15,
3811
- mkdirSync as mkdirSync2,
3812
- readdirSync as readdirSync10,
3813
- readFileSync as readFileSync20,
3814
- writeFileSync as writeFileSync8
3815
- } from "fs";
3816
- import { dirname as dirname3, join as join15 } from "path";
3817
- function writeScaffoldFile(dest, body, opts) {
3818
- if (opts.dry)
3819
- return "dry";
3820
- if (!opts.force && existsSync15(dest))
3821
- return "skip";
3822
- mkdirSync2(dirname3(dest), { recursive: true });
3823
- writeFileSync8(dest, body, "utf8");
3824
- return "write";
3825
- }
3826
- function readBundled(relPath) {
3827
- const full = join15(packageRoot(), "templates", relPath);
3828
- if (!existsSync15(full)) {
3829
- throw new Error(`\u0428\u0430\u0431\u043B\u043E\u043D \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${relPath}`);
3830
- }
3831
- return readFileSync20(full, "utf8");
3832
- }
3833
-
3834
- // src/commands/docs.ts
3835
- async function cmdDocs(parsed) {
3836
- const cwd = resolve4(parsed.options.dir || process.cwd());
3837
- const dry = hasFlag(parsed, "dry");
3838
- const force = hasFlag(parsed, "force") || !hasFlag(parsed, "keep");
3839
- const lib = hasFlag(parsed, "lib") || isElcrmLibPackage(cwd);
3840
- const kind = detectProjectKind(cwd);
3841
- const names = docsForProject(kind, lib);
3842
- console.log(`${dry ? "[dry] " : ""}elcrm docs \u2192 ${cwd}/docs (${kind}${lib ? ", lib" : ""})`);
3843
- for (const name of names) {
3844
- const body = readBundled(join16("elcrm-docs", name));
3845
- const dest = join16(cwd, "docs", name);
3846
- const action = writeScaffoldFile(dest, body, { dry, force });
3847
- const mark = action === "write" ? c.green("+") : action === "skip" ? c.gray("=") : c.cyan("~");
3848
- console.log(` ${mark} docs/${name}`);
3849
- }
3850
- if (kind === "server" && !lib) {
3851
- console.log(c.gray("\u0441\u0435\u0440\u0432\u0435\u0440: SERVER, CLI, API, SOCKET"));
3852
- }
3853
- console.log("\u0414\u0430\u043B\u044C\u0448\u0435: elcrm cursor \u2014 \u043F\u0440\u0430\u0432\u0438\u043B\u0430 \u0432 .cursor/rules (+ .cursor/docs)");
3854
- }
3855
-
3856
- // src/commands/cursor.ts
3857
- import { join as join17, resolve as resolve5 } from "path";
3858
- var ALWAYS = ["elcrm.mdc", "elcrm-packages.mdc", "elcrm-jsdoc.mdc"];
3859
- var UI = [
3860
- "elcrm-ui.mdc",
3861
- "elcrm-module.mdc",
3862
- "elcrm-theme.mdc",
3863
- "elcrm-tokens.mdc"
3864
- ];
3865
- var SERVER = ["elcrm-server.mdc", "elcrm-rpc.mdc"];
3866
- var LIB = ["elcrm-lib.mdc"];
3867
- function writeOne(dest, body, dry, force, label) {
3868
- const action = writeScaffoldFile(dest, body, { dry, force });
3869
- const mark = action === "write" ? c.green("+") : action === "skip" ? c.gray("=") : c.cyan("~");
3870
- console.log(` ${mark} ${label}`);
3871
- }
3872
- async function cmdCursor(parsed) {
3873
- const cwd = resolve5(parsed.options.dir || process.cwd());
3874
- const dry = hasFlag(parsed, "dry");
3875
- const force = hasFlag(parsed, "force") || !hasFlag(parsed, "keep");
3876
- const lib = hasFlag(parsed, "lib") || isElcrmLibPackage(cwd);
3877
- const kind = detectProjectKind(cwd);
3878
- const ruleNames = [
3879
- ...ALWAYS,
3880
- ...kind === "server" ? [] : UI,
3881
- ...kind === "front" ? [] : SERVER,
3882
- ...lib ? LIB : []
3883
- ];
3884
- console.log(`${dry ? "[dry] " : ""}elcrm cursor \u2192 ${cwd}/.cursor (${kind}${lib ? ", lib" : ""})`);
3885
- for (const name of ruleNames) {
3886
- let body = readBundled(join17("elcrm-cursor", "rules", name));
3887
- if (kind === "server" && (name === "elcrm-server.mdc" || name === "elcrm-rpc.mdc")) {
3888
- body = body.replace("alwaysApply: false", "alwaysApply: true").replace(/^globs:.*\n/m, "");
3889
- }
3890
- writeOne(join17(cwd, ".cursor", "rules", name), body, dry, force, `.cursor/rules/${name}`);
3891
- }
3892
- const docs = docsForProject(kind, lib);
3893
- for (const name of docs) {
3894
- const body = readBundled(join17("elcrm-docs", name));
3895
- writeOne(join17(cwd, ".cursor", "docs", name), body, dry, force, `.cursor/docs/${name}`);
3896
- }
3897
- writeOne(join17(cwd, "AGENTS.md"), readBundled(join17("elcrm-cursor", "AGENTS.md")), dry, force, "AGENTS.md");
3898
- console.log(c.gray("\u0410\u0433\u0435\u043D\u0442: AGENTS.md + rules (\u0443\u0437\u043A\u0438\u0435 glob) + .cursor/docs. \u0421\u0434\u0430\u0447\u0430: elcrm test."));
3899
- }
3900
-
3901
4436
  // src/index.ts
3902
4437
  function cliVersion() {
3903
4438
  const here = dirname4(fileURLToPath2(import.meta.url));
@@ -3906,7 +4441,7 @@ function cliVersion() {
3906
4441
  join18(here, "package.json")
3907
4442
  ]) {
3908
4443
  try {
3909
- const v = JSON.parse(readFileSync21(p, "utf8")).version;
4444
+ const v = JSON.parse(readFileSync23(p, "utf8")).version;
3910
4445
  if (typeof v === "string" && v)
3911
4446
  return v;
3912
4447
  } catch {}
@@ -3928,7 +4463,7 @@ function showHelp() {
3928
4463
  \u041A\u043E\u043C\u0430\u043D\u0434\u044B:
3929
4464
  update [--dry] [--core] [--no-install] [--fix] [--test]
3930
4465
  bun i -g elcrm@latest + @elcrm/* \u2192 install
3931
- --fix = migrate front + sync CSS (\u0442\u043E\u043A\u0435\u043D\u044B, \u2212\u0431\u0438\u0442\u044B\u0435 @import) + css-sanitize
4466
+ --fix = doctor --fix (migrate + css)
3932
4467
  --test = elcrm test
3933
4468
 
3934
4469
  css [--dry] [--dir=\u2026]
@@ -3943,22 +4478,24 @@ function showHelp() {
3943
4478
  --lib: \u0447\u0435\u043A\u043B\u0438\u0441\u0442 \u0430\u0432\u0442\u043E\u0440\u0430 @elcrm/*
3944
4479
 
3945
4480
  migrate <name> [--dry] | migrate --list
3946
- front|ui = \u043F\u0430\u043A\u0435\u0442 \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0439 (aliases, tokens, components, css-sanitize, size, \u2026)
3947
- \u043F\u043E \u043E\u0434\u043D\u043E\u0439: form-aliases, form-tokens, components, css-sanitize, size-sml,
3948
- field-border, modal-create, legacy-hacks, socket-server
4481
+ front|ui = \u043F\u0430\u043A\u0435\u0442 \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0439 (aliases, form-native, native-html, tokens, \u2026)
4482
+ \u043F\u043E \u043E\u0434\u043D\u043E\u0439: form-aliases, form-native, native-html, form-tokens, components,
4483
+ css-sanitize, size-sml, field-border, modal-create,
4484
+ legacy-hacks, socket-server
3949
4485
 
3950
4486
  audit [--fix] [--dry] [--dir=\u2026]
3951
- \u0421\u0442\u0430\u0440\u044B\u0435 API, \u0431\u0438\u0442\u044B\u0435 @import, orphan color-mix; --fix = migrate front
4487
+ \u0421\u0442\u0430\u0440\u044B\u0435 API, native input, prop native, orphan color-mix; --fix = migrate front
3952
4488
  \u041F\u043E\u0441\u043B\u0435 --fix \u0434\u043B\u044F imports: elcrm css
3953
4489
 
3954
- doctor [--fix]
3955
- \u0421\u0442\u0430\u043D\u0434\u0430\u0440\u0442\u044B; \u043D\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 --fix \u2192 bunx elcrm-server update
4490
+ doctor [--dry] [--docs] [--test] [--hacks] [--dir=\u2026]
4491
+ \u041D\u0430\u0432\u0435\u0434\u0435\u043D\u0438\u0435 \u043F\u043E\u0440\u044F\u0434\u043A\u0430 (\u043F\u0440\u043E\u0441\u0442\u043E \u0432\u044B\u0437\u0432\u0430\u0442\u044C). \u041F\u0440\u0435\u0432\u044C\u044E: --dry
4492
+ package.json \u2192 migrate front \u2192 css. --docs / --test \u043F\u043E \u0436\u0435\u043B\u0430\u043D\u0438\u044E
3956
4493
 
3957
- test [dir] [--front|--server] [--no-scripts]
3958
- \u0421\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u0430\u043D\u0430\u043B\u0438\u0437 + scripts \u043F\u0440\u043E\u0435\u043A\u0442\u0430
4494
+ test [dir] [--front|--server] [--no-scripts] [--fix]
4495
+ \u0421\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u0430\u043D\u0430\u043B\u0438\u0437 + scripts; --fix = migrate front \u043F\u0435\u0440\u0435\u0434 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u043E\u0439
3959
4496
 
3960
- create <name> [--template app|lib|panel] [--no-install]
3961
- init [--template app|lib|panel] [--force] [--no-install]
4497
+ create <name> [--template app|lib|panel|orbit] [--no-install]
4498
+ init [--template app|lib|panel|orbit] [--force] [--no-install]
3962
4499
  build [--client] [--url=\u2026] [--name=\u2026] [--dir=\u2026] [--folder=\u2026]
3963
4500
  postbuild [--dir=./dist]
3964
4501
  uuid
@@ -3966,6 +4503,8 @@ function showHelp() {
3966
4503
  \u0424\u043B\u0430\u0433\u0438: -v/--version, -h/--help
3967
4504
 
3968
4505
  \u041F\u0440\u0438\u043C\u0435\u0440\u044B:
4506
+ elcrm doctor
4507
+ elcrm doctor --docs --test
3969
4508
  elcrm update --fix --test && elcrm css && elcrm docs && elcrm cursor
3970
4509
  elcrm css # ENOENT @elcrm/\u2026/light.css \u2192 \u0441\u043D\u0438\u043C\u0435\u0442 \u0431\u0438\u0442\u044B\u0439 import
3971
4510
  elcrm audit