@ant-design/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2308 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command, Option } from "commander";
5
+
6
+ // src/types.ts
7
+ function localize(en, zh, lang) {
8
+ if (lang === "zh" && zh) return zh;
9
+ return en || "";
10
+ }
11
+
12
+ // src/data/loader.ts
13
+ import { readFileSync, existsSync } from "fs";
14
+ import { join, dirname } from "path";
15
+ import { fileURLToPath } from "url";
16
+ var __dirname = dirname(fileURLToPath(import.meta.url));
17
+ function getDataPath() {
18
+ const probe = "v5.json";
19
+ const candidates = [
20
+ join(__dirname, "..", "data"),
21
+ // from dist/
22
+ join(__dirname, "..", "..", "data")
23
+ // from src/data/
24
+ ];
25
+ return candidates.find((p) => existsSync(join(p, probe))) ?? candidates[0];
26
+ }
27
+ function loadMetadata(majorVersion) {
28
+ const dataPath = join(getDataPath(), `${majorVersion}.json`);
29
+ try {
30
+ return JSON.parse(readFileSync(dataPath, "utf-8"));
31
+ } catch (err) {
32
+ if (err instanceof SyntaxError) {
33
+ process.stderr.write(`[antd-cli] Warning: data file may be corrupted: ${dataPath}
34
+ `);
35
+ }
36
+ return {
37
+ version: majorVersion,
38
+ majorVersion,
39
+ components: []
40
+ };
41
+ }
42
+ }
43
+ function findComponent(store, name) {
44
+ return store.components.find(
45
+ (c) => c.name.toLowerCase() === name.toLowerCase()
46
+ );
47
+ }
48
+ function getAllComponentNames(store) {
49
+ return store.components.map((c) => c.name);
50
+ }
51
+
52
+ // src/data/version.ts
53
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
54
+ import { join as join2 } from "path";
55
+ var FALLBACK_VERSION = "5.24.0";
56
+ var FALLBACK_MAJOR = "v5";
57
+ function detectVersion(flagVersion) {
58
+ if (flagVersion) {
59
+ return {
60
+ version: flagVersion,
61
+ majorVersion: toMajor(flagVersion),
62
+ source: "flag"
63
+ };
64
+ }
65
+ const nmPath = join2(process.cwd(), "node_modules", "antd", "package.json");
66
+ if (existsSync2(nmPath)) {
67
+ try {
68
+ const pkg = JSON.parse(readFileSync2(nmPath, "utf-8"));
69
+ if (pkg.version) {
70
+ return {
71
+ version: pkg.version,
72
+ majorVersion: toMajor(pkg.version),
73
+ source: "node_modules"
74
+ };
75
+ }
76
+ } catch {
77
+ }
78
+ }
79
+ const pkgPath = join2(process.cwd(), "package.json");
80
+ if (existsSync2(pkgPath)) {
81
+ try {
82
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
83
+ const depVersion = pkg.dependencies?.antd || pkg.devDependencies?.antd || pkg.peerDependencies?.antd;
84
+ if (depVersion) {
85
+ const cleaned = depVersion.replace(/[\^~>=<\s]/g, "");
86
+ return {
87
+ version: cleaned,
88
+ majorVersion: toMajor(cleaned),
89
+ source: "package.json"
90
+ };
91
+ }
92
+ } catch {
93
+ }
94
+ }
95
+ return {
96
+ version: FALLBACK_VERSION,
97
+ majorVersion: FALLBACK_MAJOR,
98
+ source: "fallback"
99
+ };
100
+ }
101
+ function toMajor(version) {
102
+ const major = version.split(".")[0];
103
+ return `v${major}`;
104
+ }
105
+ function compare(a, b) {
106
+ const pa = a.split(".").map(Number);
107
+ const pb = b.split(".").map(Number);
108
+ for (let i = 0; i < 3; i++) {
109
+ const na = pa[i] || 0;
110
+ const nb = pb[i] || 0;
111
+ if (na < nb) return -1;
112
+ if (na > nb) return 1;
113
+ }
114
+ return 0;
115
+ }
116
+
117
+ // src/output/formatter.ts
118
+ function formatOutput(data, format) {
119
+ switch (format) {
120
+ case "json":
121
+ return JSON.stringify(data, null, 2);
122
+ case "markdown":
123
+ return formatAsMarkdown(data);
124
+ case "text":
125
+ default:
126
+ return formatAsText(data);
127
+ }
128
+ }
129
+ function formatAsText(data) {
130
+ if (typeof data === "string") return data;
131
+ if (Array.isArray(data)) return data.map((item) => formatAsText(item)).join("\n");
132
+ if (typeof data === "object" && data !== null) {
133
+ return Object.entries(data).map(([key, value]) => {
134
+ if (Array.isArray(value)) {
135
+ return `${key}:
136
+ ${value.map((v) => ` ${formatAsText(v)}`).join("\n")}`;
137
+ }
138
+ return `${key}: ${value}`;
139
+ }).join("\n");
140
+ }
141
+ return String(data);
142
+ }
143
+ function formatAsMarkdown(data) {
144
+ if (typeof data === "string") return data;
145
+ if (typeof data === "object" && data !== null) {
146
+ return "```json\n" + JSON.stringify(data, null, 2) + "\n```";
147
+ }
148
+ return String(data);
149
+ }
150
+ function formatTable(headers, rows, format) {
151
+ if (format === "json") {
152
+ return JSON.stringify(
153
+ rows.map((row) => {
154
+ const obj = {};
155
+ headers.forEach((h, i) => {
156
+ obj[h] = row[i];
157
+ });
158
+ return obj;
159
+ }),
160
+ null,
161
+ 2
162
+ );
163
+ }
164
+ if (format === "markdown") {
165
+ const escPipe = (s) => s.replace(/\|/g, "\\|");
166
+ const headerLine2 = "| " + headers.map(escPipe).join(" | ") + " |";
167
+ const separator2 = "| " + headers.map(() => "---").join(" | ") + " |";
168
+ const bodyLines2 = rows.map((row) => "| " + row.map(escPipe).join(" | ") + " |");
169
+ return [headerLine2, separator2, ...bodyLines2].join("\n");
170
+ }
171
+ const colWidths = headers.map(
172
+ (h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || "").length))
173
+ );
174
+ const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(" ");
175
+ const separator = colWidths.map((w) => "-".repeat(w)).join(" ");
176
+ const bodyLines = rows.map(
177
+ (row) => row.map((cell, i) => (cell || "").padEnd(colWidths[i])).join(" ")
178
+ );
179
+ return [headerLine, separator, ...bodyLines].join("\n");
180
+ }
181
+ function output(data, format) {
182
+ console.log(formatOutput(data, format));
183
+ }
184
+
185
+ // src/commands/list.ts
186
+ function registerListCommand(program2) {
187
+ program2.command("list").description("List all components with descriptions and categories").action(() => {
188
+ const opts = program2.opts();
189
+ const versionInfo = detectVersion(opts.version);
190
+ const store = loadMetadata(versionInfo.majorVersion);
191
+ const lang = opts.lang;
192
+ if (store.components.length === 0) {
193
+ console.log("No component data available.");
194
+ return;
195
+ }
196
+ if (opts.format === "json") {
197
+ const data = store.components.map((c) => ({
198
+ name: c.name,
199
+ category: localize(c.category, c.categoryZh, lang),
200
+ description: localize(c.description, c.descriptionZh, lang)
201
+ }));
202
+ output(data, "json");
203
+ return;
204
+ }
205
+ const categories = /* @__PURE__ */ new Map();
206
+ for (const comp of store.components) {
207
+ const cat = localize(comp.category, comp.categoryZh, lang) || "Other";
208
+ if (!categories.has(cat)) categories.set(cat, []);
209
+ categories.get(cat).push(comp);
210
+ }
211
+ if (opts.format === "markdown") {
212
+ const lines = [`# antd Components (${versionInfo.version})`, ""];
213
+ for (const [category, components] of categories) {
214
+ lines.push(`## ${category}`, "");
215
+ for (const comp of components) {
216
+ const desc = localize(comp.description, comp.descriptionZh, lang);
217
+ lines.push(`- **${comp.name}** \u2014 ${desc}`);
218
+ }
219
+ lines.push("");
220
+ }
221
+ console.log(lines.join("\n"));
222
+ return;
223
+ }
224
+ const headers = ["Component", "Category", "Description"];
225
+ const rows = store.components.map((c) => [
226
+ c.name,
227
+ localize(c.category, c.categoryZh, lang),
228
+ localize(c.description, c.descriptionZh, lang)
229
+ ]);
230
+ console.log(formatTable(headers, rows, "text"));
231
+ });
232
+ }
233
+
234
+ // src/output/error.ts
235
+ var ErrorCodes = {
236
+ COMPONENT_NOT_FOUND: "COMPONENT_NOT_FOUND",
237
+ VERSION_NOT_FOUND: "VERSION_NOT_FOUND",
238
+ NO_PROJECT_DETECTED: "NO_PROJECT_DETECTED",
239
+ METADATA_FETCH_FAILED: "METADATA_FETCH_FAILED",
240
+ UNSUPPORTED_VERSION_FEATURE: "UNSUPPORTED_VERSION_FEATURE",
241
+ DEMO_NOT_FOUND: "DEMO_NOT_FOUND",
242
+ INVALID_ARGUMENT: "INVALID_ARGUMENT"
243
+ };
244
+ function createError(code, message, suggestion) {
245
+ return { error: true, code, message, suggestion };
246
+ }
247
+ function printError(err, format) {
248
+ if (format === "json") {
249
+ console.error(JSON.stringify(err, null, 2));
250
+ } else {
251
+ console.error(`Error: ${err.message}`);
252
+ if (err.suggestion) {
253
+ console.error(`Suggestion: ${err.suggestion}`);
254
+ }
255
+ }
256
+ }
257
+ function fuzzyMatch(input, candidates) {
258
+ if (candidates.length === 0) return void 0;
259
+ const lower = input.toLowerCase();
260
+ const exact = candidates.find((c) => c.toLowerCase() === lower);
261
+ if (exact) return exact;
262
+ const prefix = candidates.find((c) => c.toLowerCase().startsWith(lower));
263
+ if (prefix) return prefix;
264
+ const contains = candidates.find((c) => c.toLowerCase().includes(lower));
265
+ if (contains) return contains;
266
+ let best;
267
+ let bestDist = Infinity;
268
+ for (const candidate of candidates) {
269
+ const dist = levenshtein(lower, candidate.toLowerCase());
270
+ if (dist < bestDist && dist <= 3) {
271
+ bestDist = dist;
272
+ best = candidate;
273
+ }
274
+ }
275
+ return best;
276
+ }
277
+ function levenshtein(a, b) {
278
+ const m = a.length;
279
+ const n = b.length;
280
+ const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
281
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
282
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
283
+ for (let i = 1; i <= m; i++) {
284
+ for (let j = 1; j <= n; j++) {
285
+ dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
286
+ }
287
+ }
288
+ return dp[m][n];
289
+ }
290
+
291
+ // src/commands/info.ts
292
+ function registerInfoCommand(program2) {
293
+ program2.command("info <component>").description("Query component API: props, type definitions, default values").action((component) => {
294
+ const opts = program2.opts();
295
+ const lang = opts.lang;
296
+ const versionInfo = detectVersion(opts.version);
297
+ const store = loadMetadata(versionInfo.majorVersion);
298
+ const comp = findComponent(store, component);
299
+ if (!comp) {
300
+ const names = getAllComponentNames(store);
301
+ const suggestion = fuzzyMatch(component, names);
302
+ const err = createError(
303
+ ErrorCodes.COMPONENT_NOT_FOUND,
304
+ `Component '${component}' not found`,
305
+ suggestion ? `Did you mean '${suggestion}'?` : void 0
306
+ );
307
+ printError(err, opts.format);
308
+ process.exitCode = 1;
309
+ return;
310
+ }
311
+ const desc = localize(comp.description, comp.descriptionZh, lang);
312
+ const whenToUse = localize(comp.whenToUse, comp.whenToUseZh, lang);
313
+ if (opts.format === "json") {
314
+ if (opts.detail) {
315
+ output(
316
+ {
317
+ name: comp.name,
318
+ description: desc,
319
+ whenToUse: whenToUse || "",
320
+ props: comp.props.map((p) => ({
321
+ ...p,
322
+ description: localize(p.description, p.descriptionZh, lang)
323
+ })),
324
+ methods: comp.methods || [],
325
+ related: comp.related || [],
326
+ faq: comp.faq || []
327
+ },
328
+ "json"
329
+ );
330
+ } else {
331
+ output(
332
+ {
333
+ name: comp.name,
334
+ description: desc,
335
+ props: comp.props.map((p) => ({
336
+ name: p.name,
337
+ type: p.type,
338
+ default: p.default
339
+ }))
340
+ },
341
+ "json"
342
+ );
343
+ }
344
+ return;
345
+ }
346
+ console.log(`${comp.name} \u2014 ${desc}`);
347
+ if (opts.detail && whenToUse) {
348
+ console.log(`
349
+ When to use: ${whenToUse}`);
350
+ }
351
+ console.log("");
352
+ const headers = opts.detail ? ["Property", "Type", "Default", "Description"] : ["Property", "Type", "Default"];
353
+ const rows = comp.props.map(
354
+ (p) => opts.detail ? [p.name, p.type, p.default, localize(p.description, p.descriptionZh, lang) || "-"] : [p.name, p.type, p.default]
355
+ );
356
+ console.log(formatTable(headers, rows, opts.format === "markdown" ? "markdown" : "text"));
357
+ if (opts.detail && comp.related && comp.related.length > 0) {
358
+ console.log(`
359
+ Related: ${comp.related.join(", ")}`);
360
+ }
361
+ });
362
+ }
363
+
364
+ // src/commands/demo.ts
365
+ function registerDemoCommand(program2) {
366
+ program2.command("demo <component> [name]").description("Get demo source code for a component").action((component, name) => {
367
+ const opts = program2.opts();
368
+ const versionInfo = detectVersion(opts.version);
369
+ const store = loadMetadata(versionInfo.majorVersion);
370
+ const comp = findComponent(store, component);
371
+ if (!comp) {
372
+ const names = getAllComponentNames(store);
373
+ const suggestion = fuzzyMatch(component, names);
374
+ const err = createError(
375
+ ErrorCodes.COMPONENT_NOT_FOUND,
376
+ `Component '${component}' not found`,
377
+ suggestion ? `Did you mean '${suggestion}'?` : void 0
378
+ );
379
+ printError(err, opts.format);
380
+ process.exitCode = 1;
381
+ return;
382
+ }
383
+ const demos = comp.demos || [];
384
+ if (!name) {
385
+ if (opts.format === "json") {
386
+ output(
387
+ {
388
+ component: comp.name,
389
+ demos: demos.map((d) => ({
390
+ name: d.name,
391
+ title: d.title,
392
+ description: d.description
393
+ }))
394
+ },
395
+ "json"
396
+ );
397
+ } else {
398
+ console.log(`${comp.name} Demos:`);
399
+ console.log("");
400
+ for (const demo2 of demos) {
401
+ console.log(` ${demo2.name} \u2014 ${demo2.title}`);
402
+ console.log(` ${demo2.description}`);
403
+ }
404
+ if (demos.length === 0) {
405
+ console.log(" No demos available.");
406
+ }
407
+ }
408
+ return;
409
+ }
410
+ const demo = demos.find((d) => d.name.toLowerCase() === name.toLowerCase());
411
+ if (!demo) {
412
+ const demoNames = demos.map((d) => d.name);
413
+ const suggestion = fuzzyMatch(name, demoNames);
414
+ const err = createError(
415
+ ErrorCodes.DEMO_NOT_FOUND,
416
+ `Demo '${name}' not found for ${comp.name}`,
417
+ suggestion ? `Did you mean '${suggestion}'?` : `Available demos: ${demoNames.join(", ") || "none"}`
418
+ );
419
+ printError(err, opts.format);
420
+ process.exitCode = 1;
421
+ return;
422
+ }
423
+ if (opts.format === "json") {
424
+ output(
425
+ {
426
+ component: comp.name,
427
+ demo: demo.name,
428
+ title: demo.title,
429
+ description: demo.description,
430
+ code: demo.code
431
+ },
432
+ "json"
433
+ );
434
+ } else {
435
+ console.log(`${comp.name} / ${demo.title}`);
436
+ console.log(`${demo.description}`);
437
+ console.log("");
438
+ console.log(demo.code);
439
+ }
440
+ });
441
+ }
442
+
443
+ // src/commands/token.ts
444
+ function registerTokenCommand(program2) {
445
+ program2.command("token [component]").description("Query Design Tokens (global or component-level)").action((component) => {
446
+ const opts = program2.opts();
447
+ const versionInfo = detectVersion(opts.version);
448
+ if (versionInfo.majorVersion === "v4") {
449
+ const err = createError(
450
+ ErrorCodes.UNSUPPORTED_VERSION_FEATURE,
451
+ "Design Token system is not available in antd v4",
452
+ "Upgrade to antd v5+ to use Design Tokens"
453
+ );
454
+ printError(err, opts.format);
455
+ process.exitCode = 1;
456
+ return;
457
+ }
458
+ const store = loadMetadata(versionInfo.majorVersion);
459
+ if (!component) {
460
+ const globalTokens = store.globalTokens || [];
461
+ if (globalTokens.length === 0) {
462
+ console.log("No global token data available.");
463
+ return;
464
+ }
465
+ if (opts.format === "json") {
466
+ output({ tokens: globalTokens }, "json");
467
+ return;
468
+ }
469
+ console.log("Global Design Tokens:");
470
+ console.log("");
471
+ const headers2 = ["Token", "Type", "Default", "Description"];
472
+ const rows2 = globalTokens.map((t) => [t.name, t.type, t.default, t.description || "-"]);
473
+ console.log(formatTable(headers2, rows2, opts.format === "markdown" ? "markdown" : "text"));
474
+ return;
475
+ }
476
+ const comp = findComponent(store, component);
477
+ if (!comp) {
478
+ const names = getAllComponentNames(store);
479
+ const suggestion = fuzzyMatch(component, names);
480
+ const err = createError(
481
+ ErrorCodes.COMPONENT_NOT_FOUND,
482
+ `Component '${component}' not found`,
483
+ suggestion ? `Did you mean '${suggestion}'?` : void 0
484
+ );
485
+ printError(err, opts.format);
486
+ process.exitCode = 1;
487
+ return;
488
+ }
489
+ const tokens = comp.tokens || [];
490
+ if (tokens.length === 0) {
491
+ console.log(`No component tokens available for ${comp.name}.`);
492
+ return;
493
+ }
494
+ if (opts.format === "json") {
495
+ output({ component: comp.name, tokens }, "json");
496
+ return;
497
+ }
498
+ console.log(`${comp.name} Component Tokens:`);
499
+ console.log("");
500
+ const headers = ["Token", "Type", "Default"];
501
+ const rows = tokens.map((t) => [t.name, t.type, t.default]);
502
+ console.log(formatTable(headers, rows, opts.format === "markdown" ? "markdown" : "text"));
503
+ });
504
+ }
505
+
506
+ // src/commands/semantic.ts
507
+ function registerSemanticCommand(program2) {
508
+ program2.command("semantic <component>").description("Query the semantic customization structure of a component").action((component) => {
509
+ const opts = program2.opts();
510
+ const versionInfo = detectVersion(opts.version);
511
+ const store = loadMetadata(versionInfo.majorVersion);
512
+ const comp = findComponent(store, component);
513
+ if (!comp) {
514
+ const names = getAllComponentNames(store);
515
+ const suggestion = fuzzyMatch(component, names);
516
+ const err = createError(
517
+ ErrorCodes.COMPONENT_NOT_FOUND,
518
+ `Component '${component}' not found`,
519
+ suggestion ? `Did you mean '${suggestion}'?` : void 0
520
+ );
521
+ printError(err, opts.format);
522
+ process.exitCode = 1;
523
+ return;
524
+ }
525
+ const structure = comp.semanticStructure || [];
526
+ if (structure.length === 0) {
527
+ console.log(`No semantic structure data available for ${comp.name}.`);
528
+ return;
529
+ }
530
+ if (opts.format === "json") {
531
+ output({ name: comp.name, semanticStructure: structure }, "json");
532
+ return;
533
+ }
534
+ console.log(`${comp.name} Semantic Structure:`);
535
+ for (let i = 0; i < structure.length; i++) {
536
+ const prefix = i === structure.length - 1 ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
537
+ console.log(`${prefix} ${structure[i].key} # ${structure[i].description}`);
538
+ }
539
+ console.log("");
540
+ console.log("Usage:");
541
+ console.log(` <${comp.name} classNames={{ ${structure[0]?.key}: 'my-${structure[0]?.key}' }} />`);
542
+ console.log(` <${comp.name} styles={{ ${structure[0]?.key}: { background: '#fff' } }} />`);
543
+ });
544
+ }
545
+
546
+ // src/commands/diff.ts
547
+ function parseVersionRange(version) {
548
+ if (version.includes("..")) {
549
+ const [from, to] = version.split("..");
550
+ return { from, to };
551
+ }
552
+ return { exact: version };
553
+ }
554
+ function filterEntries(changelog, versionArg) {
555
+ if (!versionArg) return changelog.slice(0, 5);
556
+ const parsed = parseVersionRange(versionArg);
557
+ if ("exact" in parsed) {
558
+ return changelog.filter((e) => e.version === parsed.exact);
559
+ }
560
+ return changelog.filter(
561
+ (e) => compare(e.version, parsed.from) >= 0 && compare(e.version, parsed.to) <= 0
562
+ );
563
+ }
564
+ var EMOJI_MAP = {
565
+ feature: "\u{1F195}",
566
+ fix: "\u{1F41E}",
567
+ style: "\u{1F484}",
568
+ deprecation: "\u{1F5D1}",
569
+ breaking: "\u{1F525}",
570
+ other: "\u{1F6E0}"
571
+ };
572
+ function printChangelog(entries, format, versionArg) {
573
+ if (format === "json") {
574
+ output(versionArg ? entries : { latest: entries }, "json");
575
+ return;
576
+ }
577
+ for (const entry of entries) {
578
+ console.log(`## ${entry.version} (${entry.date})`);
579
+ console.log("");
580
+ for (const change of entry.changes) {
581
+ const emoji = EMOJI_MAP[change.type] || "\u2022";
582
+ console.log(` ${emoji} ${change.component} ${change.description}`);
583
+ }
584
+ console.log("");
585
+ }
586
+ }
587
+ function toMajor2(version) {
588
+ return `v${version.split(".")[0]}`;
589
+ }
590
+ function diffProps(oldProps, newProps) {
591
+ const oldMap = new Map(oldProps.map((p) => [p.name, p]));
592
+ const newMap = new Map(newProps.map((p) => [p.name, p]));
593
+ const added = [];
594
+ const removed = [];
595
+ const changed = [];
596
+ for (const [name, prop] of newMap) {
597
+ if (!oldMap.has(name)) {
598
+ added.push({ name, type: prop.type });
599
+ } else {
600
+ const oldProp = oldMap.get(name);
601
+ if (oldProp.type !== prop.type) {
602
+ changed.push({ name, change: `${oldProp.type} \u2192 ${prop.type}` });
603
+ }
604
+ }
605
+ }
606
+ for (const [name, prop] of oldMap) {
607
+ if (!newMap.has(name)) {
608
+ const possibleRename = [...newMap.values()].find(
609
+ (p) => p.type === prop.type && !oldMap.has(p.name)
610
+ );
611
+ removed.push({ name, type: prop.type, replacement: possibleRename?.name });
612
+ }
613
+ }
614
+ return { added, removed, changed };
615
+ }
616
+ function runApiDiff(v1, v2, component, opts) {
617
+ const major1 = toMajor2(v1);
618
+ const major2 = toMajor2(v2);
619
+ const store1 = loadMetadata(major1);
620
+ const store2 = loadMetadata(major2);
621
+ if (store1.components.length === 0) {
622
+ printError(createError(ErrorCodes.VERSION_NOT_FOUND, `No data available for version ${v1} (${major1})`), opts.format);
623
+ process.exitCode = 1;
624
+ return;
625
+ }
626
+ if (store2.components.length === 0) {
627
+ printError(createError(ErrorCodes.VERSION_NOT_FOUND, `No data available for version ${v2} (${major2})`), opts.format);
628
+ process.exitCode = 1;
629
+ return;
630
+ }
631
+ const componentsToCompare = component ? [component] : [.../* @__PURE__ */ new Set([...getAllComponentNames(store1), ...getAllComponentNames(store2)])];
632
+ if (component) {
633
+ const c1 = findComponent(store1, component);
634
+ const c2 = findComponent(store2, component);
635
+ if (!c1 && !c2) {
636
+ const allNames = [.../* @__PURE__ */ new Set([...getAllComponentNames(store1), ...getAllComponentNames(store2)])];
637
+ const suggestion = fuzzyMatch(component, allNames);
638
+ printError(
639
+ createError(ErrorCodes.COMPONENT_NOT_FOUND, `Component '${component}' not found in either version`, suggestion ? `Did you mean '${suggestion}'?` : void 0),
640
+ opts.format
641
+ );
642
+ process.exitCode = 1;
643
+ return;
644
+ }
645
+ }
646
+ const diffs = [];
647
+ for (const name of componentsToCompare) {
648
+ const c1 = findComponent(store1, name);
649
+ const c2 = findComponent(store2, name);
650
+ if (!c1 && c2) {
651
+ diffs.push({ component: name, added: c2.props.map((p) => ({ name: p.name, type: p.type })), removed: [], changed: [] });
652
+ } else if (c1 && !c2) {
653
+ diffs.push({ component: name, added: [], removed: c1.props.map((p) => ({ name: p.name, type: p.type })), changed: [] });
654
+ } else if (c1 && c2) {
655
+ const { added, removed, changed } = diffProps(c1.props, c2.props);
656
+ if (added.length > 0 || removed.length > 0 || changed.length > 0) {
657
+ diffs.push({ component: name, added, removed, changed });
658
+ }
659
+ }
660
+ }
661
+ if (opts.format === "json") {
662
+ const result = component && diffs.length > 0 ? { from: v1, to: v2, ...diffs[0] } : { from: v1, to: v2, diffs };
663
+ output(result, "json");
664
+ return;
665
+ }
666
+ if (diffs.length === 0) {
667
+ console.log(`No API differences found between ${v1} and ${v2}.`);
668
+ return;
669
+ }
670
+ console.log(`API Diff: ${v1} \u2192 ${v2}`);
671
+ console.log("");
672
+ for (const diff of diffs) {
673
+ console.log(` ${diff.component}:`);
674
+ for (const p of diff.added) console.log(` + ${p.name}: ${p.type}`);
675
+ for (const p of diff.removed) {
676
+ const note = p.replacement ? ` (replaced by ${p.replacement})` : "";
677
+ console.log(` - ${p.name}: ${p.type}${note}`);
678
+ }
679
+ for (const p of diff.changed) console.log(` ~ ${p.name}: ${p.change}`);
680
+ console.log("");
681
+ }
682
+ }
683
+ function registerChangelogCommand(program2) {
684
+ program2.command("changelog [v1] [v2] [component]").description("Query changelog or compare API differences between versions").action((v1, v2, component) => {
685
+ const opts = program2.opts();
686
+ const isChangelogMode = !v2 || v1 && v1.includes("..");
687
+ if (isChangelogMode) {
688
+ const v1ForDetect = v1?.includes("..") ? v1.split("..")[0] : v1;
689
+ const versionForDetect = opts.version ?? (v1ForDetect && /^\d+\.\d+\.\d+$/.test(v1ForDetect) ? v1ForDetect : void 0);
690
+ const versionInfo = detectVersion(versionForDetect);
691
+ const store = loadMetadata(versionInfo.majorVersion);
692
+ const changelog = store.changelog || [];
693
+ if (changelog.length === 0) {
694
+ console.log("No changelog data available.");
695
+ return;
696
+ }
697
+ const entries = filterEntries(changelog, v1);
698
+ if (entries.length === 0) {
699
+ const err = createError(
700
+ ErrorCodes.VERSION_NOT_FOUND,
701
+ `No changelog entries found for "${v1}"`,
702
+ `Available versions: ${changelog.map((e) => e.version).join(", ")}`
703
+ );
704
+ printError(err, opts.format);
705
+ process.exitCode = 1;
706
+ return;
707
+ }
708
+ printChangelog(entries, opts.format, v1);
709
+ } else {
710
+ if (compare(v1, v2) > 0) {
711
+ const err = createError(
712
+ ErrorCodes.INVALID_ARGUMENT,
713
+ `Version order is invalid: "${v1}" is newer than "${v2}"`,
714
+ `Did you mean: antd changelog ${v2} ${v1}${component ? " " + component : ""}?`
715
+ );
716
+ printError(err, opts.format);
717
+ process.exitCode = 1;
718
+ return;
719
+ }
720
+ runApiDiff(v1, v2, component, opts);
721
+ }
722
+ });
723
+ }
724
+
725
+ // src/commands/doctor.ts
726
+ import { readFileSync as readFileSync4, readdirSync as readdirSync2 } from "fs";
727
+ import { join as join4 } from "path";
728
+
729
+ // src/utils/scan.ts
730
+ import { readFileSync as readFileSync3, readdirSync, statSync } from "fs";
731
+ import { join as join3, extname } from "path";
732
+ var SCAN_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
733
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".umi"]);
734
+ var ANTD_IMPORT_RE = /import\s+\{([^}]+)\}\s+from\s+['"]antd(?:\/[^'"]*)?['"]/g;
735
+ function collectFiles(dir) {
736
+ const files = [];
737
+ try {
738
+ const stat = statSync(dir);
739
+ if (stat.isFile()) return [dir];
740
+ } catch {
741
+ return [];
742
+ }
743
+ try {
744
+ const entries = readdirSync(dir, { withFileTypes: true });
745
+ for (const entry of entries) {
746
+ if (SKIP_DIRS.has(entry.name)) continue;
747
+ const fullPath = join3(dir, entry.name);
748
+ if (entry.isDirectory()) {
749
+ files.push(...collectFiles(fullPath));
750
+ } else if (SCAN_EXTENSIONS.has(extname(entry.name))) {
751
+ files.push(fullPath);
752
+ }
753
+ }
754
+ } catch {
755
+ }
756
+ return files;
757
+ }
758
+ function parseAntdImports(content) {
759
+ const names = [];
760
+ ANTD_IMPORT_RE.lastIndex = 0;
761
+ let match;
762
+ while ((match = ANTD_IMPORT_RE.exec(content)) !== null) {
763
+ const parsed = match[1].split(",").map((n) => n.trim()).filter(Boolean);
764
+ names.push(...parsed);
765
+ }
766
+ return names;
767
+ }
768
+ function readJson(path) {
769
+ try {
770
+ return JSON.parse(readFileSync3(path, "utf-8"));
771
+ } catch {
772
+ return null;
773
+ }
774
+ }
775
+
776
+ // src/commands/doctor.ts
777
+ function buildContext(cwd) {
778
+ const antdPkg = readJson(join4(cwd, "node_modules", "antd", "package.json"));
779
+ const antdMajor = antdPkg ? parseInt(antdPkg.version.split(".")[0], 10) : 0;
780
+ const projectPkg = readJson(join4(cwd, "package.json"));
781
+ const reactPkg = readJson(join4(cwd, "node_modules", "react", "package.json"));
782
+ return { cwd, antdPkg, antdMajor, projectPkg, reactPkg };
783
+ }
784
+ function checkAntdInstalled(ctx) {
785
+ if (!ctx.antdPkg) {
786
+ return {
787
+ name: "antd-installed",
788
+ status: "fail",
789
+ severity: "error",
790
+ message: "antd is not installed in node_modules",
791
+ suggestion: "Run `npm install antd` or `yarn add antd`"
792
+ };
793
+ }
794
+ return {
795
+ name: "antd-installed",
796
+ status: "pass",
797
+ message: `antd ${ctx.antdPkg.version || "unknown"} is installed`
798
+ };
799
+ }
800
+ function checkReactCompat(ctx) {
801
+ if (!ctx.antdPkg || !ctx.reactPkg) {
802
+ return {
803
+ name: "react-compat",
804
+ status: "warn",
805
+ severity: "warning",
806
+ message: "Cannot verify React compatibility \u2014 antd or react not found in node_modules"
807
+ };
808
+ }
809
+ const reactMajor = parseInt(ctx.reactPkg.version.split(".")[0], 10);
810
+ if (ctx.antdMajor >= 5 && reactMajor < 18) {
811
+ return {
812
+ name: "react-compat",
813
+ status: "fail",
814
+ severity: "error",
815
+ message: `React ${ctx.reactPkg.version} may not be compatible with antd ${ctx.antdPkg.version}`,
816
+ suggestion: "antd 5.x requires React 18+. Consider upgrading React."
817
+ };
818
+ }
819
+ return {
820
+ name: "react-compat",
821
+ status: "pass",
822
+ message: `React ${ctx.reactPkg.version} is compatible with antd ${ctx.antdPkg.version}`
823
+ };
824
+ }
825
+ function checkDuplicateInstall(ctx) {
826
+ const versions = [];
827
+ if (ctx.antdPkg?.version) versions.push(ctx.antdPkg.version);
828
+ const nmDir = join4(ctx.cwd, "node_modules");
829
+ try {
830
+ const entries = readdirSync2(nmDir);
831
+ for (const entry of entries) {
832
+ if (entry.startsWith(".") || entry === "antd") continue;
833
+ const nestedPkg = readJson(join4(nmDir, entry, "node_modules", "antd", "package.json"));
834
+ if (nestedPkg?.version && !versions.includes(nestedPkg.version)) {
835
+ versions.push(nestedPkg.version);
836
+ }
837
+ }
838
+ } catch {
839
+ }
840
+ if (versions.length > 1) {
841
+ return {
842
+ name: "duplicate-install",
843
+ status: "fail",
844
+ severity: "error",
845
+ message: `Found ${versions.length} antd installations: ${versions.join(", ")}`,
846
+ suggestion: "Run `npm dedupe` or check your dependency tree"
847
+ };
848
+ }
849
+ return {
850
+ name: "duplicate-install",
851
+ status: "pass",
852
+ message: "No duplicate antd installations detected"
853
+ };
854
+ }
855
+ function checkThemeConfig(ctx) {
856
+ if (ctx.antdMajor >= 5) {
857
+ const lessOverrides = readJson(join4(ctx.cwd, "theme", "antd.less"));
858
+ const modifyVars = ctx.projectPkg?.theme;
859
+ if (lessOverrides !== null || modifyVars) {
860
+ return {
861
+ name: "theme-config",
862
+ status: "warn",
863
+ severity: "warning",
864
+ message: "Found Less-based theme customization, but antd v5+ uses Design Tokens",
865
+ suggestion: "Migrate Less variables to ConfigProvider theme tokens. See https://ant.design/docs/react/migrate-less-variables"
866
+ };
867
+ }
868
+ }
869
+ return {
870
+ name: "theme-config",
871
+ status: "pass",
872
+ message: "No theme configuration issues detected"
873
+ };
874
+ }
875
+ function checkBabelPlugins(ctx) {
876
+ const babelConfigs = [".babelrc", "babel.config.js", "babel.config.json"];
877
+ let hasBabelPluginImport = false;
878
+ for (const configName of babelConfigs) {
879
+ try {
880
+ const content = readFileSync4(join4(ctx.cwd, configName), "utf-8");
881
+ if (content.includes("babel-plugin-import") || content.includes("import, { libraryName")) {
882
+ hasBabelPluginImport = true;
883
+ break;
884
+ }
885
+ } catch {
886
+ }
887
+ }
888
+ if (!hasBabelPluginImport && ctx.projectPkg?.babel) {
889
+ const babelStr = JSON.stringify(ctx.projectPkg.babel);
890
+ if (babelStr.includes("babel-plugin-import")) {
891
+ hasBabelPluginImport = true;
892
+ }
893
+ }
894
+ if (hasBabelPluginImport && ctx.antdMajor >= 5) {
895
+ return {
896
+ name: "babel-plugin",
897
+ status: "warn",
898
+ severity: "warning",
899
+ message: "babel-plugin-import is configured but not needed for antd v5+",
900
+ suggestion: "antd v5+ supports tree-shaking natively. Remove babel-plugin-import for antd to reduce build complexity."
901
+ };
902
+ }
903
+ return {
904
+ name: "babel-plugin",
905
+ status: "pass",
906
+ message: "No problematic Babel/webpack antd plugins detected"
907
+ };
908
+ }
909
+ function checkCssInJs(ctx) {
910
+ const cssinjs = readJson(join4(ctx.cwd, "node_modules", "@ant-design", "cssinjs", "package.json"));
911
+ if (!cssinjs) {
912
+ return {
913
+ name: "cssinjs",
914
+ status: "warn",
915
+ severity: "warning",
916
+ message: "No @ant-design/cssinjs found, SSR style extraction will not work",
917
+ suggestion: "Install @ant-design/cssinjs if you need SSR support"
918
+ };
919
+ }
920
+ return {
921
+ name: "cssinjs",
922
+ status: "pass",
923
+ message: "@ant-design/cssinjs is installed"
924
+ };
925
+ }
926
+ function registerDoctorCommand(program2) {
927
+ program2.command("doctor").description("Diagnose project-level antd configuration issues").action(() => {
928
+ const opts = program2.opts();
929
+ const ctx = buildContext(process.cwd());
930
+ const checks = [
931
+ checkAntdInstalled(ctx),
932
+ checkReactCompat(ctx),
933
+ checkDuplicateInstall(ctx),
934
+ checkThemeConfig(ctx),
935
+ checkBabelPlugins(ctx),
936
+ checkCssInJs(ctx)
937
+ ];
938
+ const summary = {
939
+ pass: checks.filter((c) => c.status === "pass").length,
940
+ warn: checks.filter((c) => c.status === "warn").length,
941
+ fail: checks.filter((c) => c.status === "fail").length
942
+ };
943
+ if (opts.format === "json") {
944
+ output({ checks, summary }, "json");
945
+ return;
946
+ }
947
+ const ICONS = { pass: "\u2713", warn: "\u26A0", fail: "\u2717" };
948
+ console.log("antd Doctor\n");
949
+ for (const check of checks) {
950
+ const icon = ICONS[check.status];
951
+ console.log(` ${icon} [${check.name}] ${check.message}`);
952
+ if (check.suggestion) {
953
+ console.log(` \u2192 ${check.suggestion}`);
954
+ }
955
+ }
956
+ console.log("");
957
+ console.log(`Summary: ${summary.pass} passed, ${summary.warn} warnings, ${summary.fail} failed`);
958
+ });
959
+ }
960
+
961
+ // src/commands/usage.ts
962
+ import { readFileSync as readFileSync5 } from "fs";
963
+ var SUB_COMPONENT_RE = /\b(\w+)\.(\w+)\b/g;
964
+ function scanFile(filePath) {
965
+ const result = /* @__PURE__ */ new Map();
966
+ let content;
967
+ try {
968
+ content = readFileSync5(filePath, "utf-8");
969
+ } catch {
970
+ return result;
971
+ }
972
+ const importedNames = parseAntdImports(content);
973
+ for (const name of importedNames) {
974
+ if (!result.has(name)) {
975
+ result.set(name, { count: 0, subComponents: /* @__PURE__ */ new Map() });
976
+ }
977
+ result.get(name).count++;
978
+ }
979
+ SUB_COMPONENT_RE.lastIndex = 0;
980
+ let match;
981
+ while ((match = SUB_COMPONENT_RE.exec(content)) !== null) {
982
+ const [, parent, child] = match;
983
+ if (importedNames.includes(parent)) {
984
+ const entry = result.get(parent);
985
+ const subKey = `${parent}.${child}`;
986
+ entry.subComponents.set(subKey, (entry.subComponents.get(subKey) || 0) + 1);
987
+ }
988
+ }
989
+ return result;
990
+ }
991
+ function registerUsageCommand(program2) {
992
+ program2.command("usage [dir]").description("Scan project for antd component/API usage statistics").action((dir) => {
993
+ const opts = program2.opts();
994
+ const targetDir = dir || ".";
995
+ const files = collectFiles(targetDir);
996
+ const aggregated = /* @__PURE__ */ new Map();
997
+ for (const file of files) {
998
+ const fileUsage = scanFile(file);
999
+ for (const [name, data] of fileUsage) {
1000
+ if (!aggregated.has(name)) {
1001
+ aggregated.set(name, { imports: 0, files: /* @__PURE__ */ new Set(), subComponents: /* @__PURE__ */ new Map() });
1002
+ }
1003
+ const agg = aggregated.get(name);
1004
+ agg.imports += data.count;
1005
+ agg.files.add(file);
1006
+ for (const [sub, count] of data.subComponents) {
1007
+ agg.subComponents.set(sub, (agg.subComponents.get(sub) || 0) + count);
1008
+ }
1009
+ }
1010
+ }
1011
+ const components = [...aggregated.entries()].sort((a, b) => b[1].imports - a[1].imports).map(([name, data]) => {
1012
+ const usage = {
1013
+ name,
1014
+ imports: data.imports,
1015
+ files: [...data.files]
1016
+ };
1017
+ if (data.subComponents.size > 0) {
1018
+ usage.subComponents = Object.fromEntries(data.subComponents);
1019
+ }
1020
+ return usage;
1021
+ });
1022
+ const totalImports = components.reduce((sum, c) => sum + c.imports, 0);
1023
+ if (opts.format === "json") {
1024
+ output({
1025
+ scanned: files.length,
1026
+ components,
1027
+ summary: { totalComponents: components.length, totalImports }
1028
+ }, "json");
1029
+ return;
1030
+ }
1031
+ console.log(`Scanned ${files.length} files in ${targetDir}
1032
+ `);
1033
+ if (components.length === 0) {
1034
+ console.log("No antd imports found.");
1035
+ return;
1036
+ }
1037
+ for (const comp of components) {
1038
+ console.log(` ${comp.name} \u2014 ${comp.imports} imports across ${comp.files.length} files`);
1039
+ if (comp.subComponents) {
1040
+ for (const [sub, count] of Object.entries(comp.subComponents)) {
1041
+ console.log(` ${sub}: ${count} usages`);
1042
+ }
1043
+ }
1044
+ }
1045
+ console.log(`
1046
+ Total: ${components.length} components, ${totalImports} imports`);
1047
+ });
1048
+ }
1049
+
1050
+ // src/commands/lint.ts
1051
+ import { readFileSync as readFileSync6 } from "fs";
1052
+ function escapeRegExp(s) {
1053
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1054
+ }
1055
+ function getDeprecatedProps(store) {
1056
+ const result = /* @__PURE__ */ new Map();
1057
+ for (const comp of store.components) {
1058
+ const deprecated = comp.props.filter((p) => p.deprecated);
1059
+ if (deprecated.length > 0) {
1060
+ result.set(comp.name, deprecated.map((p) => ({
1061
+ prop: p.name,
1062
+ since: typeof p.deprecated === "string" ? p.deprecated : "unknown",
1063
+ message: `\`${p.name}\` prop is deprecated${typeof p.deprecated === "string" ? ` since ${p.deprecated}` : ""}`
1064
+ })));
1065
+ }
1066
+ }
1067
+ return result;
1068
+ }
1069
+ function hasNearbyMatch(lines, i, lookahead, pattern) {
1070
+ const end = Math.min(i + lookahead, lines.length);
1071
+ for (let j = i; j < end; j++) {
1072
+ if (pattern.test(lines[j])) return true;
1073
+ }
1074
+ return false;
1075
+ }
1076
+ function lintFile(filePath, deprecatedMap, only) {
1077
+ const issues = [];
1078
+ let content;
1079
+ try {
1080
+ content = readFileSync6(filePath, "utf-8");
1081
+ } catch {
1082
+ return [];
1083
+ }
1084
+ const lines = content.split("\n");
1085
+ const importedComponents = parseAntdImports(content);
1086
+ if (importedComponents.length === 0) return [];
1087
+ const deprecatedChecks = [];
1088
+ if (!only || only === "deprecated") {
1089
+ for (const compName of importedComponents) {
1090
+ const deprecations = deprecatedMap.get(compName);
1091
+ if (!deprecations) continue;
1092
+ for (const dep of deprecations) {
1093
+ deprecatedChecks.push({ compName, dep, regex: new RegExp(`\\b${escapeRegExp(dep.prop)}\\b\\s*[=({]`) });
1094
+ }
1095
+ }
1096
+ }
1097
+ const hasImage = importedComponents.includes("Image");
1098
+ const hasTable = importedComponents.includes("Table");
1099
+ const hasSelect = importedComponents.includes("Select") || importedComponents.includes("TreeSelect");
1100
+ for (let i = 0; i < lines.length; i++) {
1101
+ const line = lines[i];
1102
+ for (const { compName, dep, regex } of deprecatedChecks) {
1103
+ if (regex.test(line)) {
1104
+ issues.push({
1105
+ file: filePath,
1106
+ line: i + 1,
1107
+ rule: "deprecated",
1108
+ severity: "warning",
1109
+ message: `${compName} ${dep.message}`
1110
+ });
1111
+ }
1112
+ }
1113
+ if (!only || only === "best-practice") {
1114
+ if (/style\s*=\s*\{\{/.test(line) && importedComponents.some((c) => line.includes(`<${c}`))) {
1115
+ issues.push({
1116
+ file: filePath,
1117
+ line: i + 1,
1118
+ rule: "best-practice",
1119
+ severity: "warning",
1120
+ message: "Consider using `classNames` / `styles` props or Design Tokens instead of inline styles"
1121
+ });
1122
+ }
1123
+ }
1124
+ if (!only || only === "a11y") {
1125
+ if (hasImage && /<Image\b/.test(line) && !hasNearbyMatch(lines, i, 5, /alt\s*=/)) {
1126
+ issues.push({
1127
+ file: filePath,
1128
+ line: i + 1,
1129
+ rule: "a11y",
1130
+ severity: "warning",
1131
+ message: "Image component is missing `alt` prop for accessibility"
1132
+ });
1133
+ }
1134
+ if (/<\w+Icon\b/.test(line) && /onClick\s*=/.test(line) && !hasNearbyMatch(lines, i, 3, /aria-label\s*=/)) {
1135
+ issues.push({
1136
+ file: filePath,
1137
+ line: i + 1,
1138
+ rule: "a11y",
1139
+ severity: "warning",
1140
+ message: "Clickable icon should have `aria-label` for screen readers"
1141
+ });
1142
+ }
1143
+ if (/Form\.Item\b/.test(line) && !hasNearbyMatch(lines, i, 5, /(?:label|aria-label|noStyle)\s*[=]/)) {
1144
+ issues.push({
1145
+ file: filePath,
1146
+ line: i + 1,
1147
+ rule: "a11y",
1148
+ severity: "warning",
1149
+ message: "Form.Item should have a `label` prop for accessibility"
1150
+ });
1151
+ }
1152
+ }
1153
+ if (!only || only === "performance") {
1154
+ if (hasTable && /<Table\b/.test(line) && !hasNearbyMatch(lines, i, 10, /rowKey\s*=/)) {
1155
+ issues.push({
1156
+ file: filePath,
1157
+ line: i + 1,
1158
+ rule: "performance",
1159
+ severity: "warning",
1160
+ message: "Table should have explicit `rowKey` prop for optimal rendering performance"
1161
+ });
1162
+ }
1163
+ if (hasSelect && /<(?:Select|TreeSelect)\b/.test(line) && hasNearbyMatch(lines, i, 10, /virtual\s*=\s*\{?\s*false/)) {
1164
+ issues.push({
1165
+ file: filePath,
1166
+ line: i + 1,
1167
+ rule: "performance",
1168
+ severity: "warning",
1169
+ message: "Disabling `virtual` scroll on Select may cause performance issues with large datasets"
1170
+ });
1171
+ }
1172
+ if (/import\s+antd\b/.test(line) || /import\s+\*\s+as\s+\w+\s+from\s+['"]antd['"]/.test(line)) {
1173
+ issues.push({
1174
+ file: filePath,
1175
+ line: i + 1,
1176
+ rule: "performance",
1177
+ severity: "error",
1178
+ message: "Avoid wildcard import from antd. Use named imports: `import { Button } from 'antd'`"
1179
+ });
1180
+ }
1181
+ }
1182
+ }
1183
+ return issues;
1184
+ }
1185
+ function registerLintCommand(program2) {
1186
+ program2.command("lint [target]").description("Check antd usage against best practices").option("--only <category>", "Only check specific category (deprecated, a11y, performance, best-practice)").action((target, cmdOpts) => {
1187
+ const opts = program2.opts();
1188
+ const targetPath = target || ".";
1189
+ const versionInfo = detectVersion(opts.version);
1190
+ const store = loadMetadata(versionInfo.majorVersion);
1191
+ const deprecatedMap = getDeprecatedProps(store);
1192
+ const files = collectFiles(targetPath);
1193
+ const allIssues = [];
1194
+ for (const file of files) {
1195
+ allIssues.push(...lintFile(file, deprecatedMap, cmdOpts.only));
1196
+ }
1197
+ const summary = {
1198
+ total: allIssues.length,
1199
+ deprecated: allIssues.filter((i) => i.rule === "deprecated").length,
1200
+ a11y: allIssues.filter((i) => i.rule === "a11y").length,
1201
+ performance: allIssues.filter((i) => i.rule === "performance").length,
1202
+ "best-practice": allIssues.filter((i) => i.rule === "best-practice").length
1203
+ };
1204
+ if (opts.format === "json") {
1205
+ output({ issues: allIssues, summary }, "json");
1206
+ return;
1207
+ }
1208
+ if (allIssues.length === 0) {
1209
+ console.log(`Scanned ${files.length} files. No issues found.`);
1210
+ return;
1211
+ }
1212
+ console.log(`Scanned ${files.length} files. Found ${allIssues.length} issues:
1213
+ `);
1214
+ for (const issue of allIssues) {
1215
+ const icon = issue.severity === "error" ? "\u2717" : "\u26A0";
1216
+ console.log(` ${icon} ${issue.file}:${issue.line} [${issue.rule}]`);
1217
+ console.log(` ${issue.message}`);
1218
+ }
1219
+ console.log(`
1220
+ Summary: ${summary.deprecated} deprecated, ${summary.a11y} a11y, ${summary.performance} performance, ${summary["best-practice"]} best-practice`);
1221
+ });
1222
+ }
1223
+
1224
+ // src/commands/migrate-v4-to-v5.ts
1225
+ var V4_TO_V5_STEPS = [
1226
+ // Global changes
1227
+ {
1228
+ component: "Global",
1229
+ breaking: true,
1230
+ description: "Design token system replaces Less variables. All Less variable overrides must be migrated to CSS-in-JS tokens via ConfigProvider.",
1231
+ autoFixable: false,
1232
+ guide: "https://ant.design/docs/react/migrate-less-variables",
1233
+ migrationGuide: `1. Remove all Less variable overrides (modifyVars, less-loader theme config)
1234
+ 2. Wrap your app with ConfigProvider and pass a theme object
1235
+ 3. Map Less variables to Design Tokens (e.g. @primary-color \u2192 token.colorPrimary)
1236
+ 4. Remove babel-plugin-import if present (v5 supports tree-shaking natively)`,
1237
+ before: `// webpack.config.js or .umirc.ts
1238
+ theme: { '@primary-color': '#1890ff' }
1239
+
1240
+ // or .less file
1241
+ @import '~antd/dist/antd.less';
1242
+ @primary-color: #1890ff;`,
1243
+ after: `import { ConfigProvider } from 'antd';
1244
+ <ConfigProvider theme={{ token: { colorPrimary: '#1890ff' } }}>
1245
+ <App />
1246
+ </ConfigProvider>`
1247
+ },
1248
+ {
1249
+ component: "Global",
1250
+ breaking: true,
1251
+ description: "Component styles no longer include CSS reset. Wrap app with <App /> component to restore.",
1252
+ autoFixable: false,
1253
+ guide: "https://ant.design/components/app",
1254
+ searchPattern: `import\\s*\\{[^}]*\\}\\s*from\\s*['"]antd['"]`,
1255
+ migrationGuide: `1. Import App component from antd
1256
+ 2. Wrap your root component with <App />
1257
+ 3. Use App.useApp() hook to access message, notification, modal instances`,
1258
+ before: `import { Button } from 'antd';
1259
+
1260
+ const MyApp = () => <Button>Click</Button>;`,
1261
+ after: `import { App, Button } from 'antd';
1262
+
1263
+ const MyApp = () => (
1264
+ <App>
1265
+ <Button>Click</Button>
1266
+ </App>
1267
+ );`
1268
+ },
1269
+ {
1270
+ component: "Global",
1271
+ breaking: true,
1272
+ description: "Moment.js replaced by Day.js. All date-related components (DatePicker, TimePicker, Calendar) now use Day.js.",
1273
+ autoFixable: true,
1274
+ codemod: "v5-removed-component-migration",
1275
+ searchPattern: `import\\s+moment\\s+from\\s+['"]moment['"]`,
1276
+ migrationGuide: `1. Replace moment imports with dayjs
1277
+ 2. dayjs API is mostly compatible with moment
1278
+ 3. If you need locale support, import dayjs locale separately
1279
+ 4. If you need plugins (isBetween, etc.), import and extend dayjs`,
1280
+ before: `import moment from 'moment';
1281
+ <DatePicker value={moment('2024-01-01')} />`,
1282
+ after: `import dayjs from 'dayjs';
1283
+ <DatePicker value={dayjs('2024-01-01')} />`
1284
+ },
1285
+ {
1286
+ component: "Global",
1287
+ breaking: true,
1288
+ description: "babel-plugin-import is no longer needed. antd v5 supports tree-shaking natively.",
1289
+ autoFixable: false,
1290
+ searchPattern: `babel-plugin-import`,
1291
+ migrationGuide: `1. Remove babel-plugin-import from babel config
1292
+ 2. Remove related configuration in .babelrc, babel.config.js, or package.json
1293
+ 3. Direct imports like \`import { Button } from 'antd'\` work without the plugin`
1294
+ },
1295
+ {
1296
+ component: "Global",
1297
+ breaking: true,
1298
+ description: "`antd/dist/antd.css` and `antd/dist/antd.less` removed. CSS-in-JS generates styles automatically.",
1299
+ autoFixable: true,
1300
+ codemod: "v5-removed-component-migration",
1301
+ searchPattern: `import\\s+['"]antd/dist/antd\\.(css|less)['"]`,
1302
+ migrationGuide: `Remove the global CSS/Less import. Styles are now injected via CSS-in-JS automatically.`,
1303
+ before: `import 'antd/dist/antd.css';
1304
+ // or
1305
+ import 'antd/dist/antd.less';`,
1306
+ after: `// No global import needed \u2014 styles are auto-injected`
1307
+ },
1308
+ // Prop renames (visible → open)
1309
+ {
1310
+ component: "Modal",
1311
+ breaking: true,
1312
+ description: "Prop `visible` renamed to `open`",
1313
+ autoFixable: true,
1314
+ codemod: "v5-props-changed-migration",
1315
+ searchPattern: `<Modal[^>]*\\bvisible\\b`,
1316
+ before: `<Modal visible={show} onCancel={onClose}>`,
1317
+ after: `<Modal open={show} onCancel={onClose}>`,
1318
+ migrationGuide: `Search for \`<Modal\` with \`visible\` prop and rename to \`open\`. Also applies to Modal.confirm, Modal.info, etc.`
1319
+ },
1320
+ {
1321
+ component: "Drawer",
1322
+ breaking: true,
1323
+ description: "Prop `visible` renamed to `open`",
1324
+ autoFixable: true,
1325
+ codemod: "v5-props-changed-migration",
1326
+ searchPattern: `<Drawer[^>]*\\bvisible\\b`,
1327
+ before: `<Drawer visible={show} onClose={onClose}>`,
1328
+ after: `<Drawer open={show} onClose={onClose}>`
1329
+ },
1330
+ {
1331
+ component: "Tooltip",
1332
+ breaking: true,
1333
+ description: "Prop `visible` renamed to `open`",
1334
+ autoFixable: true,
1335
+ codemod: "v5-props-changed-migration",
1336
+ searchPattern: `<Tooltip[^>]*\\bvisible\\b`,
1337
+ before: `<Tooltip visible={show}>`,
1338
+ after: `<Tooltip open={show}>`
1339
+ },
1340
+ {
1341
+ component: "Popover",
1342
+ breaking: true,
1343
+ description: "Prop `visible` renamed to `open`",
1344
+ autoFixable: true,
1345
+ codemod: "v5-props-changed-migration",
1346
+ searchPattern: `<Popover[^>]*\\bvisible\\b`,
1347
+ before: `<Popover visible={show}>`,
1348
+ after: `<Popover open={show}>`
1349
+ },
1350
+ {
1351
+ component: "Popconfirm",
1352
+ breaking: true,
1353
+ description: "Prop `visible` renamed to `open`",
1354
+ autoFixable: true,
1355
+ codemod: "v5-props-changed-migration",
1356
+ searchPattern: `<Popconfirm[^>]*\\bvisible\\b`,
1357
+ before: `<Popconfirm visible={show}>`,
1358
+ after: `<Popconfirm open={show}>`
1359
+ },
1360
+ {
1361
+ component: "Dropdown",
1362
+ breaking: true,
1363
+ description: "Prop `visible` renamed to `open`",
1364
+ autoFixable: true,
1365
+ codemod: "v5-props-changed-migration",
1366
+ searchPattern: `<Dropdown[^>]*\\bvisible\\b`,
1367
+ before: `<Dropdown visible={show}>`,
1368
+ after: `<Dropdown open={show}>`
1369
+ },
1370
+ {
1371
+ component: "Tag",
1372
+ breaking: true,
1373
+ description: "Prop `visible` removed. Use conditional rendering instead.",
1374
+ autoFixable: false,
1375
+ searchPattern: `<Tag[^>]*\\bvisible\\b`,
1376
+ before: `<Tag visible={show}>Tag</Tag>`,
1377
+ after: `{show && <Tag>Tag</Tag>}`,
1378
+ migrationGuide: `Replace \`visible\` prop with conditional rendering using \`{condition && <Tag>...</Tag>}\`.`
1379
+ },
1380
+ // dropdownClassName → popupClassName
1381
+ {
1382
+ component: "Select",
1383
+ breaking: true,
1384
+ description: "Prop `dropdownClassName` renamed to `popupClassName`",
1385
+ autoFixable: true,
1386
+ codemod: "v5-props-changed-migration",
1387
+ searchPattern: `<Select[^>]*\\bdropdownClassName\\b`,
1388
+ before: `<Select dropdownClassName="my-dropdown" />`,
1389
+ after: `<Select popupClassName="my-dropdown" />`
1390
+ },
1391
+ {
1392
+ component: "Select",
1393
+ breaking: true,
1394
+ description: "Prop `dropdownMatchSelectWidth` renamed to `popupMatchSelectWidth`",
1395
+ autoFixable: true,
1396
+ codemod: "v5-props-changed-migration",
1397
+ searchPattern: `<Select[^>]*\\bdropdownMatchSelectWidth\\b`,
1398
+ before: `<Select dropdownMatchSelectWidth={false} />`,
1399
+ after: `<Select popupMatchSelectWidth={false} />`
1400
+ },
1401
+ {
1402
+ component: "TreeSelect",
1403
+ breaking: true,
1404
+ description: "Prop `dropdownClassName` renamed to `popupClassName`",
1405
+ autoFixable: true,
1406
+ codemod: "v5-props-changed-migration",
1407
+ searchPattern: `<TreeSelect[^>]*\\bdropdownClassName\\b`,
1408
+ before: `<TreeSelect dropdownClassName="my-dropdown" />`,
1409
+ after: `<TreeSelect popupClassName="my-dropdown" />`
1410
+ },
1411
+ {
1412
+ component: "Cascader",
1413
+ breaking: true,
1414
+ description: "Prop `dropdownClassName` renamed to `popupClassName`",
1415
+ autoFixable: true,
1416
+ codemod: "v5-props-changed-migration",
1417
+ searchPattern: `<Cascader[^>]*\\bdropdownClassName\\b`,
1418
+ before: `<Cascader dropdownClassName="my-dropdown" />`,
1419
+ after: `<Cascader popupClassName="my-dropdown" />`
1420
+ },
1421
+ {
1422
+ component: "AutoComplete",
1423
+ breaking: true,
1424
+ description: "Prop `dropdownClassName` renamed to `popupClassName`",
1425
+ autoFixable: true,
1426
+ codemod: "v5-props-changed-migration",
1427
+ searchPattern: `<AutoComplete[^>]*\\bdropdownClassName\\b`,
1428
+ before: `<AutoComplete dropdownClassName="my-dropdown" />`,
1429
+ after: `<AutoComplete popupClassName="my-dropdown" />`
1430
+ },
1431
+ {
1432
+ component: "DatePicker",
1433
+ breaking: true,
1434
+ description: "Prop `dropdownClassName` renamed to `popupClassName`",
1435
+ autoFixable: true,
1436
+ codemod: "v5-props-changed-migration",
1437
+ searchPattern: `<DatePicker[^>]*\\bdropdownClassName\\b`,
1438
+ before: `<DatePicker dropdownClassName="my-popup" />`,
1439
+ after: `<DatePicker popupClassName="my-popup" />`
1440
+ },
1441
+ {
1442
+ component: "TimePicker",
1443
+ breaking: true,
1444
+ description: "Prop `dropdownClassName` renamed to `popupClassName`",
1445
+ autoFixable: true,
1446
+ codemod: "v5-props-changed-migration",
1447
+ searchPattern: `<TimePicker[^>]*\\bdropdownClassName\\b`,
1448
+ before: `<TimePicker dropdownClassName="my-popup" />`,
1449
+ after: `<TimePicker popupClassName="my-popup" />`
1450
+ },
1451
+ {
1452
+ component: "Mentions",
1453
+ breaking: true,
1454
+ description: "Prop `dropdownClassName` renamed to `popupClassName`",
1455
+ autoFixable: true,
1456
+ codemod: "v5-props-changed-migration",
1457
+ searchPattern: `<Mentions[^>]*\\bdropdownClassName\\b`,
1458
+ before: `<Mentions dropdownClassName="my-dropdown" />`,
1459
+ after: `<Mentions popupClassName="my-dropdown" />`
1460
+ },
1461
+ // Removed components
1462
+ {
1463
+ component: "Comment",
1464
+ breaking: true,
1465
+ description: "Removed from antd. Use @ant-design/compatible instead.",
1466
+ autoFixable: false,
1467
+ guide: "https://ant.design/docs/react/migration-v5",
1468
+ searchPattern: `import\\s*\\{[^}]*\\bComment\\b[^}]*\\}\\s*from\\s*['"]antd['"]`,
1469
+ migrationGuide: `1. Install @ant-design/compatible: npm install @ant-design/compatible
1470
+ 2. Change import from 'antd' to '@ant-design/compatible'`,
1471
+ before: `import { Comment } from 'antd';`,
1472
+ after: `import { Comment } from '@ant-design/compatible';`
1473
+ },
1474
+ {
1475
+ component: "PageHeader",
1476
+ breaking: true,
1477
+ description: "Removed from antd. Use @ant-design/pro-components instead.",
1478
+ autoFixable: false,
1479
+ guide: "https://ant.design/docs/react/migration-v5",
1480
+ searchPattern: `import\\s*\\{[^}]*\\bPageHeader\\b[^}]*\\}\\s*from\\s*['"]antd['"]`,
1481
+ migrationGuide: `1. Install @ant-design/pro-components: npm install @ant-design/pro-components
1482
+ 2. Change import from 'antd' to '@ant-design/pro-components'`,
1483
+ before: `import { PageHeader } from 'antd';`,
1484
+ after: `import { PageHeader } from '@ant-design/pro-components';`
1485
+ },
1486
+ {
1487
+ component: "BackTop",
1488
+ breaking: true,
1489
+ description: "Removed. Use FloatButton.BackTop instead.",
1490
+ autoFixable: true,
1491
+ codemod: "v5-removed-component-migration",
1492
+ searchPattern: `import\\s*\\{[^}]*\\bBackTop\\b[^}]*\\}\\s*from\\s*['"]antd['"]`,
1493
+ before: `import { BackTop } from 'antd';
1494
+ <BackTop />`,
1495
+ after: `import { FloatButton } from 'antd';
1496
+ <FloatButton.BackTop />`
1497
+ },
1498
+ // API changes
1499
+ {
1500
+ component: "message",
1501
+ breaking: true,
1502
+ description: "Static methods `message.xxx()` deprecated. Use `App.useApp()` hook instead for context support.",
1503
+ autoFixable: false,
1504
+ searchPattern: `\\bmessage\\.(success|error|warning|info|loading|open)\\(`,
1505
+ migrationGuide: `1. Wrap app with <App /> component
1506
+ 2. Use const { message } = App.useApp(); in functional components
1507
+ 3. Replace static message.xxx() calls with the hook-provided instance`,
1508
+ before: `import { message } from 'antd';
1509
+ message.success('Done!');`,
1510
+ after: `import { App } from 'antd';
1511
+ const MyComponent = () => {
1512
+ const { message } = App.useApp();
1513
+ message.success('Done!');
1514
+ };`
1515
+ },
1516
+ {
1517
+ component: "notification",
1518
+ breaking: true,
1519
+ description: "Static methods `notification.xxx()` deprecated. Use `App.useApp()` hook instead.",
1520
+ autoFixable: false,
1521
+ searchPattern: `\\bnotification\\.(success|error|warning|info|open)\\(`,
1522
+ migrationGuide: `Same pattern as message \u2014 use App.useApp() to get notification instance.`,
1523
+ before: `import { notification } from 'antd';
1524
+ notification.success({ message: 'Done!' });`,
1525
+ after: `import { App } from 'antd';
1526
+ const MyComponent = () => {
1527
+ const { notification } = App.useApp();
1528
+ notification.success({ message: 'Done!' });
1529
+ };`
1530
+ },
1531
+ {
1532
+ component: "Modal",
1533
+ breaking: true,
1534
+ description: "Static methods `Modal.confirm()` etc. deprecated. Use `App.useApp()` hook instead.",
1535
+ autoFixable: false,
1536
+ searchPattern: `\\bModal\\.(confirm|info|success|error|warning)\\(`,
1537
+ migrationGuide: `Same pattern as message/notification \u2014 use App.useApp() to get modal instance.`,
1538
+ before: `import { Modal } from 'antd';
1539
+ Modal.confirm({ title: 'Sure?' });`,
1540
+ after: `import { App } from 'antd';
1541
+ const MyComponent = () => {
1542
+ const { modal } = App.useApp();
1543
+ modal.confirm({ title: 'Sure?' });
1544
+ };`
1545
+ },
1546
+ // Table
1547
+ {
1548
+ component: "Table",
1549
+ breaking: true,
1550
+ description: "Column `filterDropdown` render args changed. `confirm({ closeDropdown: false })` replaced by separate `close` function.",
1551
+ autoFixable: false,
1552
+ searchPattern: `filterDropdown.*closeDropdown`,
1553
+ migrationGuide: `Update filterDropdown render function signature to use the new \`close\` parameter instead of \`confirm({ closeDropdown: false })\`.`,
1554
+ before: `filterDropdown: ({ confirm }) => (
1555
+ <Button onClick={() => confirm({ closeDropdown: false })}>Filter</Button>
1556
+ )`,
1557
+ after: `filterDropdown: ({ confirm, close }) => (
1558
+ <Button onClick={() => close()}>Filter</Button>
1559
+ )`
1560
+ },
1561
+ // Form
1562
+ {
1563
+ component: "Form",
1564
+ breaking: false,
1565
+ description: "Prop `labelCol` and `wrapperCol` can now be set globally via ConfigProvider.",
1566
+ autoFixable: false,
1567
+ migrationGuide: `Optional improvement: move repeated labelCol/wrapperCol config to ConfigProvider for consistency.`
1568
+ },
1569
+ // Slider
1570
+ {
1571
+ component: "Slider",
1572
+ breaking: true,
1573
+ description: "Tooltip-related APIs converged into `tooltip` property. `tooltipVisible` \u2192 `tooltip.open`, `tipFormatter` \u2192 `tooltip.formatter`, etc.",
1574
+ autoFixable: false,
1575
+ searchPattern: `<Slider[^>]*\\b(tooltipVisible|tipFormatter|tooltipPlacement|getTooltipPopupContainer)\\b`,
1576
+ before: `<Slider tooltipVisible={show} tipFormatter={(v) => \`\${v}%\`} />`,
1577
+ after: `<Slider tooltip={{ open: show, formatter: (v) => \`\${v}%\` }} />`,
1578
+ migrationGuide: `Migrate Slider tooltip props:
1579
+ - tooltipVisible \u2192 tooltip.open
1580
+ - tipFormatter \u2192 tooltip.formatter
1581
+ - tooltipPlacement \u2192 tooltip.placement
1582
+ - getTooltipPopupContainer \u2192 tooltip.getPopupContainer`
1583
+ },
1584
+ // Table filterDropdownVisible
1585
+ {
1586
+ component: "Table",
1587
+ breaking: true,
1588
+ description: "Column prop `filterDropdownVisible` renamed to `filterDropdownOpen`.",
1589
+ autoFixable: true,
1590
+ codemod: "v5-props-changed-migration",
1591
+ searchPattern: `filterDropdownVisible`,
1592
+ before: `{ title: 'Name', dataIndex: 'name', filterDropdownVisible: visible }`,
1593
+ after: `{ title: 'Name', dataIndex: 'name', filterDropdownOpen: visible }`,
1594
+ migrationGuide: `Search for \`filterDropdownVisible\` in Table column definitions and rename to \`filterDropdownOpen\`.`
1595
+ },
1596
+ // Drawer style/className migration
1597
+ {
1598
+ component: "Drawer",
1599
+ breaking: true,
1600
+ description: "Drawer `style` and `className` now apply to the panel node. Use `rootStyle` and `rootClassName` for the wrapper.",
1601
+ autoFixable: false,
1602
+ searchPattern: `<Drawer[^>]*\\b(style|className)\\s*=`,
1603
+ migrationGuide: `In v5, Drawer \`style\` and \`className\` are migrated to the Drawer panel node.
1604
+ If you were using them to style the wrapper/mask, replace with \`rootStyle\` and \`rootClassName\`.`,
1605
+ before: `<Drawer className="my-drawer" style={{ zIndex: 1000 }}>`,
1606
+ after: `<Drawer rootClassName="my-drawer" rootStyle={{ zIndex: 1000 }}>`
1607
+ },
1608
+ // Notification close → destroy
1609
+ {
1610
+ component: "notification",
1611
+ breaking: true,
1612
+ description: "Static method `notification.close()` renamed to `notification.destroy()`. Static config options restricted.",
1613
+ autoFixable: false,
1614
+ searchPattern: `\\bnotification\\.close\\(`,
1615
+ before: `notification.close(key);`,
1616
+ after: `notification.destroy(key);`,
1617
+ migrationGuide: `1. Rename notification.close() to notification.destroy()
1618
+ 2. Static methods no longer allow dynamic prefixCls, maxCount, top, bottom, getContainer in open()
1619
+ 3. Use useNotification hook if you need different configurations`
1620
+ },
1621
+ // message.warn removed
1622
+ {
1623
+ component: "message",
1624
+ breaking: true,
1625
+ description: "`message.warn()` completely removed. Use `message.warning()` instead.",
1626
+ autoFixable: true,
1627
+ codemod: "v5-props-changed-migration",
1628
+ searchPattern: `\\bmessage\\.warn\\(`,
1629
+ before: `message.warn('Something went wrong');`,
1630
+ after: `message.warning('Something went wrong');`,
1631
+ migrationGuide: `Search for \`message.warn(\` and replace with \`message.warning(\`.`
1632
+ },
1633
+ // IE not supported
1634
+ {
1635
+ component: "Global",
1636
+ breaking: true,
1637
+ description: "IE browser is no longer supported. v5 uses CSS-in-JS and modern CSS features.",
1638
+ autoFixable: false,
1639
+ migrationGuide: `If your project needs IE support, you cannot upgrade to v5. Consider using @ant-design/cssinjs StyleProvider for :where selector compatibility with older browsers.`
1640
+ }
1641
+ ];
1642
+
1643
+ // src/commands/migrate-v5-to-v6.ts
1644
+ var V5_TO_V6_STEPS = [
1645
+ // ─── Global / Environment ───────────────────────────────────────────────────
1646
+ {
1647
+ component: "Global",
1648
+ breaking: true,
1649
+ description: "React >= 18 required. React 17 and earlier are no longer supported.",
1650
+ autoFixable: false,
1651
+ migrationGuide: `1. Upgrade React to 18 or above
1652
+ 2. Remove @ant-design/v5-patch-for-react-19 if present (no longer needed)`,
1653
+ searchPattern: `@ant-design/v5-patch-for-react-19`,
1654
+ before: `import '@ant-design/v5-patch-for-react-19';`,
1655
+ after: `// Remove this import \u2014 no longer needed in v6`
1656
+ },
1657
+ {
1658
+ component: "Global",
1659
+ breaking: true,
1660
+ description: "@ant-design/icons must be upgraded to v6. Icons v6 is not compatible with antd v5.",
1661
+ autoFixable: false,
1662
+ migrationGuide: `1. Run: npm install @ant-design/icons@6
1663
+ 2. @ant-design/icons@6 is NOT compatible with antd@5 \u2014 upgrade both together
1664
+ 3. If build errors occur, verify icon package version matches antd version`
1665
+ },
1666
+ {
1667
+ component: "Global",
1668
+ breaking: true,
1669
+ description: "CSS variables enabled by default. Only modern browsers supported, IE is not supported.",
1670
+ autoFixable: false,
1671
+ migrationGuide: `Verify your target browsers support CSS variables. IE and some older domestic browsers may have issues.`
1672
+ },
1673
+ {
1674
+ component: "Global",
1675
+ breaking: false,
1676
+ description: "DOM structure of many components optimized. Custom styles targeting internal DOM nodes may need adjustment.",
1677
+ autoFixable: false,
1678
+ migrationGuide: `If your project has CSS that targets component internal DOM nodes (specific selectors or hierarchy), inspect and adjust after upgrading.`
1679
+ },
1680
+ // ─── Button ─────────────────────────────────────────────────────────────────
1681
+ {
1682
+ component: "Button",
1683
+ breaking: true,
1684
+ description: 'Prop `type` split into `color` and `variant` for styling. `type="primary"` \u2192 `color="primary" variant="solid"`.',
1685
+ autoFixable: true,
1686
+ codemod: "v6-type-to-variant",
1687
+ searchPattern: `<Button[^>]*\\btype\\s*=\\s*['"](?:primary|dashed|link|text)['"]`,
1688
+ migrationGuide: `Button type prop is decomposed:
1689
+ - type="primary" \u2192 color="primary" variant="solid" (or just keep type="primary" as alias)
1690
+ - type="dashed" \u2192 variant="dashed"
1691
+ - type="link" \u2192 variant="link"
1692
+ - type="text" \u2192 variant="text"
1693
+ - type="default" \u2192 no change needed`,
1694
+ before: `<Button type="primary">Submit</Button>
1695
+ <Button type="dashed">Dashed</Button>
1696
+ <Button type="link">Link</Button>
1697
+ <Button type="text">Text</Button>`,
1698
+ after: `<Button color="primary" variant="solid">Submit</Button>
1699
+ <Button variant="dashed">Dashed</Button>
1700
+ <Button variant="link">Link</Button>
1701
+ <Button variant="text">Text</Button>`
1702
+ },
1703
+ {
1704
+ component: "Button",
1705
+ breaking: true,
1706
+ description: 'Prop `danger` removed. Use `color="danger"` instead.',
1707
+ autoFixable: true,
1708
+ codemod: "v6-danger-migration",
1709
+ searchPattern: `<Button[^>]*\\bdanger\\b`,
1710
+ before: `<Button danger>Delete</Button>
1711
+ <Button type="primary" danger>Delete</Button>`,
1712
+ after: `<Button color="danger">Delete</Button>
1713
+ <Button color="danger" variant="solid">Delete</Button>`,
1714
+ migrationGuide: `Search for all Button components with \`danger\` prop. Replace with \`color="danger"\`. If the button also had \`type="primary"\`, add \`variant="solid"\`.`
1715
+ },
1716
+ {
1717
+ component: "Button",
1718
+ breaking: true,
1719
+ description: 'Prop `ghost` removed. Use `variant="outlined"` instead.',
1720
+ autoFixable: true,
1721
+ codemod: "v6-ghost-migration",
1722
+ searchPattern: `<Button[^>]*\\bghost\\b`,
1723
+ before: `<Button ghost>Ghost</Button>
1724
+ <Button type="primary" ghost>Ghost Primary</Button>`,
1725
+ after: `<Button variant="outlined">Ghost</Button>
1726
+ <Button color="primary" variant="outlined">Ghost Primary</Button>`,
1727
+ migrationGuide: `Search for all Button components with \`ghost\` prop. Replace with \`variant="outlined"\`. Preserve the color from the original \`type\` prop.`
1728
+ },
1729
+ {
1730
+ component: "Button",
1731
+ breaking: false,
1732
+ description: "`iconPosition` deprecated, use `iconPlacement` instead.",
1733
+ autoFixable: true,
1734
+ codemod: "v6-props-changed-migration",
1735
+ searchPattern: `<Button[^>]*\\biconPosition\\b`,
1736
+ before: `<Button iconPosition="end">Click</Button>`,
1737
+ after: `<Button iconPlacement="end">Click</Button>`
1738
+ },
1739
+ {
1740
+ component: "Button.Group",
1741
+ breaking: false,
1742
+ description: "`Button.Group` deprecated, use `Space.Compact` instead.",
1743
+ autoFixable: false,
1744
+ searchPattern: `Button\\.Group`,
1745
+ before: `<Button.Group><Button>A</Button><Button>B</Button></Button.Group>`,
1746
+ after: `<Space.Compact><Button>A</Button><Button>B</Button></Space.Compact>`
1747
+ },
1748
+ // ─── Alert ──────────────────────────────────────────────────────────────────
1749
+ {
1750
+ component: "Alert",
1751
+ breaking: false,
1752
+ description: "`closeText` deprecated \u2192 `closable.closeIcon`; `message` deprecated \u2192 `title`.",
1753
+ autoFixable: false,
1754
+ searchPattern: `<Alert[^>]*\\b(closeText|message)\\s*=`,
1755
+ migrationGuide: `- closeText="Close" \u2192 closable={{ closeIcon: "Close" }}
1756
+ - message="Title" \u2192 title="Title"`
1757
+ },
1758
+ // ─── Anchor ─────────────────────────────────────────────────────────────────
1759
+ {
1760
+ component: "Anchor",
1761
+ breaking: false,
1762
+ description: "`Anchor` children usage deprecated, use `items` prop instead.",
1763
+ autoFixable: false,
1764
+ searchPattern: `<Anchor>\\s*<Anchor\\.Link`,
1765
+ migrationGuide: `Replace Anchor children with items prop array.`
1766
+ },
1767
+ // ─── AutoComplete ───────────────────────────────────────────────────────────
1768
+ {
1769
+ component: "AutoComplete",
1770
+ breaking: false,
1771
+ description: "Multiple deprecated props: `dropdownMatchSelectWidth` \u2192 `popupMatchSelectWidth`, `dropdownClassName` \u2192 `classNames.popup.root`, `dropdownRender` \u2192 `popupRender`, `dataSource` \u2192 `options`.",
1772
+ autoFixable: false,
1773
+ searchPattern: `<AutoComplete[^>]*\\b(dropdownMatchSelectWidth|dropdownClassName|dropdownStyle|dropdownRender|onDropdownVisibleChange|dataSource)\\b`,
1774
+ migrationGuide: `- dropdownMatchSelectWidth \u2192 popupMatchSelectWidth
1775
+ - dropdownClassName / popupClassName \u2192 classNames.popup.root
1776
+ - dropdownStyle \u2192 styles.popup.root
1777
+ - dropdownRender \u2192 popupRender
1778
+ - onDropdownVisibleChange \u2192 onOpenChange
1779
+ - dataSource \u2192 options`
1780
+ },
1781
+ // ─── Avatar.Group ───────────────────────────────────────────────────────────
1782
+ {
1783
+ component: "Avatar.Group",
1784
+ breaking: false,
1785
+ description: "`maxCount`/`maxStyle`/`maxPopoverPlacement`/`maxPopoverTrigger` deprecated, use `max` prop object.",
1786
+ autoFixable: false,
1787
+ searchPattern: `<Avatar\\.Group[^>]*\\b(maxCount|maxStyle|maxPopoverPlacement|maxPopoverTrigger)\\b`,
1788
+ before: `<Avatar.Group maxCount={3} maxStyle={{ color: 'red' }}>`,
1789
+ after: `<Avatar.Group max={{ count: 3, style: { color: 'red' } }}>`
1790
+ },
1791
+ // ─── Breadcrumb ─────────────────────────────────────────────────────────────
1792
+ {
1793
+ component: "Breadcrumb",
1794
+ breaking: false,
1795
+ description: "`routes` deprecated \u2192 `items`; `Breadcrumb.Item` / `Breadcrumb.Separator` deprecated \u2192 `items`.",
1796
+ autoFixable: false,
1797
+ searchPattern: `(Breadcrumb\\.Item|Breadcrumb\\.Separator|<Breadcrumb[^>]*\\broutes\\b)`,
1798
+ migrationGuide: `Replace Breadcrumb children or routes prop with items array.`
1799
+ },
1800
+ // ─── Calendar ───────────────────────────────────────────────────────────────
1801
+ {
1802
+ component: "Calendar",
1803
+ breaking: false,
1804
+ description: "`dateFullCellRender`/`dateCellRender`/`monthFullCellRender`/`monthCellRender` deprecated \u2192 `fullCellRender`/`cellRender`.",
1805
+ autoFixable: false,
1806
+ searchPattern: `<Calendar[^>]*\\b(dateFullCellRender|dateCellRender|monthFullCellRender|monthCellRender)\\b`
1807
+ },
1808
+ // ─── Card ───────────────────────────────────────────────────────────────────
1809
+ {
1810
+ component: "Card",
1811
+ breaking: false,
1812
+ description: "`headStyle` \u2192 `styles.header`; `bodyStyle` \u2192 `styles.body`; `bordered` \u2192 `variant`.",
1813
+ autoFixable: false,
1814
+ searchPattern: `<Card[^>]*\\b(headStyle|bodyStyle|bordered)\\b`,
1815
+ migrationGuide: `- headStyle \u2192 styles.header
1816
+ - bodyStyle \u2192 styles.body
1817
+ - bordered={false} \u2192 variant="borderless"`
1818
+ },
1819
+ // ─── Cascader ───────────────────────────────────────────────────────────────
1820
+ {
1821
+ component: "Cascader",
1822
+ breaking: false,
1823
+ description: "Multiple deprecated props: `dropdownClassName` \u2192 `classNames.popup.root`, `bordered` \u2192 `variant`, `onPopupVisibleChange` \u2192 `onOpenChange`.",
1824
+ autoFixable: false,
1825
+ searchPattern: `<Cascader[^>]*\\b(dropdownClassName|dropdownStyle|dropdownRender|dropdownMenuColumnStyle|onDropdownVisibleChange|onPopupVisibleChange|bordered)\\b`
1826
+ },
1827
+ // ─── Collapse ───────────────────────────────────────────────────────────────
1828
+ {
1829
+ component: "Collapse",
1830
+ breaking: false,
1831
+ description: "`destroyInactivePanel` \u2192 `destroyOnHidden`; `expandIconPosition` \u2192 `expandIconPlacement`.",
1832
+ autoFixable: false,
1833
+ searchPattern: `<Collapse[^>]*\\b(destroyInactivePanel|expandIconPosition)\\b`
1834
+ },
1835
+ // ─── DatePicker ─────────────────────────────────────────────────────────────
1836
+ {
1837
+ component: "DatePicker",
1838
+ breaking: false,
1839
+ description: "Multiple deprecated props: `popupClassName` \u2192 `classNames.popup.root`, `bordered` \u2192 `variant`, `onSelect` \u2192 `onCalendarChange`.",
1840
+ autoFixable: false,
1841
+ searchPattern: `<(DatePicker|RangePicker)[^>]*\\b(dropdownClassName|popupClassName|popupStyle|bordered|onSelect)\\b`
1842
+ },
1843
+ // ─── Descriptions ──────────────────────────────────────────────────────────
1844
+ {
1845
+ component: "Descriptions",
1846
+ breaking: false,
1847
+ description: "`labelStyle` \u2192 `styles.label`; `contentStyle` \u2192 `styles.content`.",
1848
+ autoFixable: false,
1849
+ searchPattern: `<Descriptions[^>]*\\b(labelStyle|contentStyle)\\b`
1850
+ },
1851
+ // ─── Divider ────────────────────────────────────────────────────────────────
1852
+ {
1853
+ component: "Divider",
1854
+ breaking: false,
1855
+ description: "`type` deprecated \u2192 `orientation`; `orientationMargin` \u2192 `styles.content.margin`.",
1856
+ autoFixable: false,
1857
+ searchPattern: `<Divider[^>]*\\b(type\\s*=|orientationMargin)\\b`
1858
+ },
1859
+ // ─── Drawer ─────────────────────────────────────────────────────────────────
1860
+ {
1861
+ component: "Drawer",
1862
+ breaking: false,
1863
+ description: "Style props deprecated: `headerStyle`/`bodyStyle`/`footerStyle`/`maskStyle`/`drawerStyle`/`contentWrapperStyle` \u2192 `styles.*`; `width`/`height` \u2192 `size`; `destroyInactivePanel` \u2192 `destroyOnHidden`.",
1864
+ autoFixable: false,
1865
+ searchPattern: `<Drawer[^>]*\\b(headerStyle|bodyStyle|footerStyle|contentWrapperStyle|maskStyle|drawerStyle|destroyInactivePanel)\\b`,
1866
+ migrationGuide: `- headerStyle \u2192 styles.header
1867
+ - bodyStyle \u2192 styles.body
1868
+ - footerStyle \u2192 styles.footer
1869
+ - contentWrapperStyle \u2192 styles.wrapper
1870
+ - maskStyle \u2192 styles.mask
1871
+ - drawerStyle \u2192 styles.section
1872
+ - width/height \u2192 size
1873
+ - destroyInactivePanel \u2192 destroyOnHidden`
1874
+ },
1875
+ // ─── Dropdown ───────────────────────────────────────────────────────────────
1876
+ {
1877
+ component: "Dropdown",
1878
+ breaking: false,
1879
+ description: "`overlayClassName` \u2192 `classNames.root`; `overlayStyle` \u2192 `styles.root`; `destroyPopupOnHide` \u2192 `destroyOnHidden`. `Dropdown.Button` deprecated \u2192 `Space.Compact + Dropdown + Button`.",
1880
+ autoFixable: false,
1881
+ searchPattern: `(Dropdown\\.Button|<Dropdown[^>]*\\b(overlayClassName|overlayStyle|destroyPopupOnHide|dropdownRender)\\b)`
1882
+ },
1883
+ // ─── Form ───────────────────────────────────────────────────────────────────
1884
+ {
1885
+ component: "Form",
1886
+ breaking: true,
1887
+ description: "`onFinish` no longer includes all Form.List data. Unregistered Form.Item fields are excluded.",
1888
+ autoFixable: false,
1889
+ migrationGuide: `In v5, Form.List was treated as a single Field, causing onFinish to include all data.
1890
+ In v6, only registered Form.Item fields are included.
1891
+ You no longer need getFieldsValue({ strict: true }) to filter.`,
1892
+ before: `const onFinish = (values) => {
1893
+ const realValues = getFieldsValue({ strict: true });
1894
+ };`,
1895
+ after: `const onFinish = (values) => {
1896
+ const realValues = values; // Already filtered in v6
1897
+ };`
1898
+ },
1899
+ // ─── Image ──────────────────────────────────────────────────────────────────
1900
+ {
1901
+ component: "Image",
1902
+ breaking: false,
1903
+ description: "`visible` \u2192 `open`; `onVisibleChange` \u2192 `onOpenChange`; `wrapperStyle` \u2192 `styles.root`; `maskClassName` \u2192 `classNames.cover`; `toolbarRender` \u2192 `actionsRender`.",
1904
+ autoFixable: false,
1905
+ searchPattern: `<Image[^>]*\\b(visible|onVisibleChange|wrapperStyle|maskClassName|rootClassName|toolbarRender)\\b`
1906
+ },
1907
+ // ─── Input.Group ────────────────────────────────────────────────────────────
1908
+ {
1909
+ component: "Input.Group",
1910
+ breaking: false,
1911
+ description: "`Input.Group` deprecated, use `Space.Compact` instead.",
1912
+ autoFixable: false,
1913
+ searchPattern: `Input\\.Group`,
1914
+ before: `<Input.Group compact><Input /><Input /></Input.Group>`,
1915
+ after: `<Space.Compact><Input /><Input /></Space.Compact>`
1916
+ },
1917
+ // ─── Menu ───────────────────────────────────────────────────────────────────
1918
+ {
1919
+ component: "Menu",
1920
+ breaking: false,
1921
+ description: "`children` deprecated, use `items` prop instead.",
1922
+ autoFixable: false,
1923
+ searchPattern: `<Menu>\\s*<Menu\\.Item`
1924
+ },
1925
+ // ─── Modal ──────────────────────────────────────────────────────────────────
1926
+ {
1927
+ component: "Modal",
1928
+ breaking: false,
1929
+ description: "`bodyStyle` \u2192 `styles.body`; `maskStyle` \u2192 `styles.mask`; `destroyOnClose` \u2192 `destroyOnHidden`.",
1930
+ autoFixable: false,
1931
+ searchPattern: `<Modal[^>]*\\b(bodyStyle|maskStyle|destroyOnClose)\\b`
1932
+ },
1933
+ // ─── Notification ──────────────────────────────────────────────────────────
1934
+ {
1935
+ component: "notification",
1936
+ breaking: false,
1937
+ description: "`btn` deprecated \u2192 `actions`; `message` deprecated \u2192 `title`.",
1938
+ autoFixable: false,
1939
+ searchPattern: `notification\\.(open|success|error|info|warning)\\(\\{[^}]*(\\bbtn\\b|\\bmessage\\b)`
1940
+ },
1941
+ // ─── Progress ───────────────────────────────────────────────────────────────
1942
+ {
1943
+ component: "Progress",
1944
+ breaking: false,
1945
+ description: "`strokeWidth`/`width` \u2192 `size`; `trailColor` \u2192 `railColor`; `gapPosition` \u2192 `gapPlacement`.",
1946
+ autoFixable: false,
1947
+ searchPattern: `<Progress[^>]*\\b(strokeWidth|trailColor|gapPosition)\\b`
1948
+ },
1949
+ // ─── Select ─────────────────────────────────────────────────────────────────
1950
+ {
1951
+ component: "Select",
1952
+ breaking: false,
1953
+ description: "Multiple deprecated props: `dropdownClassName` \u2192 `classNames.popup.root`, `dropdownRender` \u2192 `popupRender`, `bordered` \u2192 `variant`, `onDropdownVisibleChange` \u2192 `onOpenChange`.",
1954
+ autoFixable: false,
1955
+ searchPattern: `<Select[^>]*\\b(dropdownClassName|dropdownStyle|dropdownRender|onDropdownVisibleChange|bordered)\\b`
1956
+ },
1957
+ // ─── Slider ─────────────────────────────────────────────────────────────────
1958
+ {
1959
+ component: "Slider",
1960
+ breaking: false,
1961
+ description: "Tooltip props deprecated: `tooltipPrefixCls` \u2192 `tooltip.prefixCls`, `tipFormatter` \u2192 `tooltip.formatter`, `tooltipVisible` \u2192 `tooltip.open`, `tooltipPlacement` \u2192 `tooltip.placement`.",
1962
+ autoFixable: false,
1963
+ searchPattern: `<Slider[^>]*\\b(tooltipPrefixCls|tipFormatter|tooltipVisible|tooltipPlacement|getTooltipPopupContainer)\\b`
1964
+ },
1965
+ // ─── Space ──────────────────────────────────────────────────────────────────
1966
+ {
1967
+ component: "Space",
1968
+ breaking: false,
1969
+ description: "`direction` \u2192 `orientation`; `split` \u2192 `separator`.",
1970
+ autoFixable: false,
1971
+ searchPattern: `<Space[^>]*\\b(direction|split)\\s*=`
1972
+ },
1973
+ // ─── Steps ──────────────────────────────────────────────────────────────────
1974
+ {
1975
+ component: "Steps",
1976
+ breaking: false,
1977
+ description: '`labelPlacement` \u2192 `titlePlacement`; `progressDot` \u2192 `type="dot"`; `direction` \u2192 `orientation`; `items.description` \u2192 `items.content`.',
1978
+ autoFixable: false,
1979
+ searchPattern: `<Steps[^>]*\\b(labelPlacement|progressDot|direction)\\b`
1980
+ },
1981
+ // ─── Table ──────────────────────────────────────────────────────────────────
1982
+ {
1983
+ component: "Table",
1984
+ breaking: false,
1985
+ description: "`pagination.position` \u2192 `pagination.placement`; `filterDropdownOpen` \u2192 `filterDropdownProps.open`; `onFilterDropdownOpenChange` \u2192 `filterDropdownProps.onOpenChange`.",
1986
+ autoFixable: false,
1987
+ searchPattern: `(filterDropdownOpen|onFilterDropdownOpenChange|filterCheckall)`
1988
+ },
1989
+ // ─── Tabs ───────────────────────────────────────────────────────────────────
1990
+ {
1991
+ component: "Tabs",
1992
+ breaking: false,
1993
+ description: "`tabPosition` \u2192 `tabPlacement`; `destroyInactiveTabPane` \u2192 `destroyOnHidden`; `Tabs.TabPane` deprecated \u2192 `items`.",
1994
+ autoFixable: false,
1995
+ searchPattern: `(Tabs\\.TabPane|<Tabs[^>]*\\b(tabPosition|destroyInactiveTabPane)\\b)`
1996
+ },
1997
+ // ─── Tag ────────────────────────────────────────────────────────────────────
1998
+ {
1999
+ component: "Tag",
2000
+ breaking: true,
2001
+ description: 'Default trailing margin removed. `bordered={false}` \u2192 `variant="filled"`; `color="xxx-inverse"` \u2192 `variant="solid"`.',
2002
+ autoFixable: false,
2003
+ searchPattern: `<Tag[^>]*\\b(bordered|color\\s*=\\s*['"][^'"]*-inverse)`,
2004
+ migrationGuide: `- Tag trailing margin removed. Add margin via ConfigProvider tag.styles if needed.
2005
+ - bordered={false} \u2192 variant="filled"
2006
+ - color="xxx-inverse" \u2192 variant="solid"`
2007
+ },
2008
+ // ─── Timeline ───────────────────────────────────────────────────────────────
2009
+ {
2010
+ component: "Timeline",
2011
+ breaking: false,
2012
+ description: "`Timeline.Item` deprecated \u2192 `items`; `pending`/`pendingDot` deprecated \u2192 `items`; `mode=left|right` \u2192 `mode=start|end`.",
2013
+ autoFixable: false,
2014
+ searchPattern: `(Timeline\\.Item|<Timeline[^>]*\\b(pending|pendingDot|mode\\s*=\\s*['"](?:left|right))\\b)`
2015
+ },
2016
+ // ─── Tooltip ────────────────────────────────────────────────────────────────
2017
+ {
2018
+ component: "Tooltip",
2019
+ breaking: false,
2020
+ description: "`overlayStyle` \u2192 `styles.root`; `overlayInnerStyle` \u2192 `styles.container`; `overlayClassName` \u2192 `classNames.root`; `destroyTooltipOnHide` \u2192 `destroyOnHidden`.",
2021
+ autoFixable: false,
2022
+ searchPattern: `<Tooltip[^>]*\\b(overlayStyle|overlayInnerStyle|overlayClassName|destroyTooltipOnHide)\\b`
2023
+ },
2024
+ // ─── Transfer ───────────────────────────────────────────────────────────────
2025
+ {
2026
+ component: "Transfer",
2027
+ breaking: false,
2028
+ description: "`listStyle` \u2192 `styles.section`; `operationStyle` \u2192 `styles.actions`; `operations` \u2192 `actions`.",
2029
+ autoFixable: false,
2030
+ searchPattern: `<Transfer[^>]*\\b(listStyle|operationStyle|operations)\\b`
2031
+ },
2032
+ // ─── TreeSelect ─────────────────────────────────────────────────────────────
2033
+ {
2034
+ component: "TreeSelect",
2035
+ breaking: false,
2036
+ description: "Multiple deprecated props: `dropdownClassName` \u2192 `classNames.popup.root`, `dropdownRender` \u2192 `popupRender`, `bordered` \u2192 `variant`, `onDropdownVisibleChange` \u2192 `onOpenChange`.",
2037
+ autoFixable: false,
2038
+ searchPattern: `<TreeSelect[^>]*\\b(dropdownClassName|dropdownStyle|dropdownRender|onDropdownVisibleChange|bordered)\\b`
2039
+ },
2040
+ // ─── Overlay: Modal, Drawer mask blur ──────────────────────────────────────
2041
+ {
2042
+ component: "Global",
2043
+ breaking: false,
2044
+ description: "Modal/Drawer mask blur enabled by default. Disable via ConfigProvider if undesired.",
2045
+ autoFixable: false,
2046
+ migrationGuide: `If you don't want the mask blur effect:
2047
+ <ConfigProvider modal={{ mask: { blur: false } }} drawer={{ mask: { blur: false } }}>
2048
+ <App />
2049
+ </ConfigProvider>`
2050
+ }
2051
+ ];
2052
+
2053
+ // src/commands/migrate.ts
2054
+ var MIGRATION_GUIDES = {
2055
+ "4-5": V4_TO_V5_STEPS,
2056
+ "5-6": V5_TO_V6_STEPS
2057
+ };
2058
+ function formatText(from, to, steps) {
2059
+ const autoFixable = steps.filter((s) => s.autoFixable).length;
2060
+ const manual = steps.length - autoFixable;
2061
+ console.log(`Migration Guide: v${from} \u2192 v${to}
2062
+ `);
2063
+ const grouped = groupByComponent(steps);
2064
+ for (const [comp, compSteps] of grouped) {
2065
+ console.log(` ${comp}:`);
2066
+ for (const step of compSteps) {
2067
+ const icon = step.autoFixable ? "\u{1F527}" : "\u{1F4DD}";
2068
+ const breaking = step.breaking ? " [BREAKING]" : "";
2069
+ console.log(` ${icon}${breaking} ${step.description}`);
2070
+ if (step.guide) console.log(` Guide: ${step.guide}`);
2071
+ }
2072
+ console.log("");
2073
+ }
2074
+ console.log(`Total: ${steps.length} steps (${autoFixable} auto-fixable, ${manual} manual)`);
2075
+ }
2076
+ function formatMarkdown(from, to, steps) {
2077
+ const lines = [];
2078
+ const autoFixable = steps.filter((s) => s.autoFixable).length;
2079
+ const manual = steps.length - autoFixable;
2080
+ lines.push(`# Migration Guide: antd v${from} \u2192 v${to}`);
2081
+ lines.push("");
2082
+ lines.push(`> ${steps.length} total steps: ${autoFixable} auto-fixable, ${manual} manual`);
2083
+ lines.push("");
2084
+ const grouped = groupByComponent(steps);
2085
+ for (const [comp, compSteps] of grouped) {
2086
+ lines.push(`## ${comp}`);
2087
+ lines.push("");
2088
+ for (const step of compSteps) {
2089
+ const badge = step.breaking ? "**BREAKING**" : "non-breaking";
2090
+ const fix = step.autoFixable ? "\u{1F527} auto-fixable" : "\u{1F4DD} manual";
2091
+ lines.push(`### ${step.description}`);
2092
+ lines.push("");
2093
+ lines.push(`${badge} | ${fix}`);
2094
+ lines.push("");
2095
+ if (step.migrationGuide) {
2096
+ lines.push(step.migrationGuide);
2097
+ lines.push("");
2098
+ }
2099
+ if (step.before && step.after) {
2100
+ lines.push("**Before:**");
2101
+ lines.push("```tsx");
2102
+ lines.push(step.before);
2103
+ lines.push("```");
2104
+ lines.push("");
2105
+ lines.push("**After:**");
2106
+ lines.push("```tsx");
2107
+ lines.push(step.after);
2108
+ lines.push("```");
2109
+ lines.push("");
2110
+ }
2111
+ if (step.searchPattern) {
2112
+ lines.push(`**Search pattern:** \`${step.searchPattern}\``);
2113
+ lines.push("");
2114
+ }
2115
+ if (step.guide) {
2116
+ lines.push(`**Reference:** ${step.guide}`);
2117
+ lines.push("");
2118
+ }
2119
+ }
2120
+ }
2121
+ console.log(lines.join("\n"));
2122
+ }
2123
+ function formatJson(from, to, steps) {
2124
+ const autoFixable = steps.filter((s) => s.autoFixable).length;
2125
+ const manual = steps.length - autoFixable;
2126
+ output({
2127
+ from,
2128
+ to,
2129
+ steps,
2130
+ summary: { total: steps.length, autoFixable, manual }
2131
+ }, "json");
2132
+ }
2133
+ function formatApplyPrompt(from, to, steps, dir, format) {
2134
+ const fixableSteps = steps.filter((s) => s.autoFixable && s.searchPattern);
2135
+ const manualSteps = steps.filter((s) => !s.autoFixable);
2136
+ if (format === "json") {
2137
+ output({
2138
+ from,
2139
+ to,
2140
+ targetDir: dir,
2141
+ autoFixSteps: fixableSteps.map((s) => ({
2142
+ component: s.component,
2143
+ description: s.description,
2144
+ searchPattern: s.searchPattern,
2145
+ before: s.before,
2146
+ after: s.after,
2147
+ migrationGuide: s.migrationGuide
2148
+ })),
2149
+ manualSteps: manualSteps.map((s) => ({
2150
+ component: s.component,
2151
+ description: s.description,
2152
+ guide: s.guide,
2153
+ migrationGuide: s.migrationGuide,
2154
+ before: s.before,
2155
+ after: s.after
2156
+ })),
2157
+ summary: {
2158
+ totalAutoFix: fixableSteps.length,
2159
+ totalManual: manualSteps.length
2160
+ }
2161
+ }, "json");
2162
+ return;
2163
+ }
2164
+ const lines = [];
2165
+ lines.push(`# Auto-Migration Prompt: antd v${from} \u2192 v${to}`);
2166
+ lines.push(`# Target directory: ${dir}`);
2167
+ lines.push("");
2168
+ lines.push("## Instructions for Code Agent");
2169
+ lines.push("");
2170
+ lines.push(`Scan all .ts/.tsx/.js/.jsx files in \`${dir}\` and apply the following migrations.`);
2171
+ lines.push("For each file changed, verify the fix compiles correctly before moving on.");
2172
+ lines.push("");
2173
+ if (fixableSteps.length > 0) {
2174
+ lines.push("## Auto-fixable Changes");
2175
+ lines.push("");
2176
+ for (let i = 0; i < fixableSteps.length; i++) {
2177
+ const step = fixableSteps[i];
2178
+ lines.push(`### ${i + 1}. ${step.component}: ${step.description}`);
2179
+ lines.push("");
2180
+ if (step.searchPattern) {
2181
+ lines.push(`**Search regex:** \`${step.searchPattern}\``);
2182
+ lines.push("");
2183
+ }
2184
+ if (step.migrationGuide) {
2185
+ lines.push(step.migrationGuide);
2186
+ lines.push("");
2187
+ }
2188
+ if (step.before && step.after) {
2189
+ lines.push("**Before:**");
2190
+ lines.push("```tsx");
2191
+ lines.push(step.before);
2192
+ lines.push("```");
2193
+ lines.push("**After:**");
2194
+ lines.push("```tsx");
2195
+ lines.push(step.after);
2196
+ lines.push("```");
2197
+ lines.push("");
2198
+ }
2199
+ }
2200
+ }
2201
+ if (manualSteps.length > 0) {
2202
+ lines.push("## Manual Changes (require human review)");
2203
+ lines.push("");
2204
+ for (let i = 0; i < manualSteps.length; i++) {
2205
+ const step = manualSteps[i];
2206
+ lines.push(`### ${i + 1}. ${step.component}: ${step.description}`);
2207
+ lines.push("");
2208
+ if (step.migrationGuide) {
2209
+ lines.push(step.migrationGuide);
2210
+ lines.push("");
2211
+ }
2212
+ if (step.before && step.after) {
2213
+ lines.push("**Before:**");
2214
+ lines.push("```tsx");
2215
+ lines.push(step.before);
2216
+ lines.push("```");
2217
+ lines.push("**After:**");
2218
+ lines.push("```tsx");
2219
+ lines.push(step.after);
2220
+ lines.push("```");
2221
+ lines.push("");
2222
+ }
2223
+ if (step.guide) {
2224
+ lines.push(`**Reference:** ${step.guide}`);
2225
+ lines.push("");
2226
+ }
2227
+ }
2228
+ }
2229
+ console.log(lines.join("\n"));
2230
+ }
2231
+ function groupByComponent(steps) {
2232
+ const map = /* @__PURE__ */ new Map();
2233
+ for (const step of steps) {
2234
+ const arr = map.get(step.component) || [];
2235
+ arr.push(step);
2236
+ map.set(step.component, arr);
2237
+ }
2238
+ return map;
2239
+ }
2240
+ function registerMigrateCommand(program2) {
2241
+ program2.command("migrate <from> <to>").description("Version migration guide with optional auto-fix").option("--component <name>", "Component-specific migration guide").option("--apply <dir>", "Generate migration prompts for scanning a directory").option("--confirm", "Confirm auto-fix (required with --apply)").action((from, to, cmdOpts) => {
2242
+ const opts = program2.opts();
2243
+ const key = `${from}-${to}`;
2244
+ let steps = MIGRATION_GUIDES[key];
2245
+ if (!steps) {
2246
+ const err = createError(
2247
+ ErrorCodes.INVALID_ARGUMENT,
2248
+ `No migration guide available for v${from} \u2192 v${to}`,
2249
+ `Available migrations: ${Object.keys(MIGRATION_GUIDES).map((k) => `v${k.replace("-", " \u2192 v")}`).join(", ")}`
2250
+ );
2251
+ printError(err, opts.format);
2252
+ process.exitCode = 1;
2253
+ return;
2254
+ }
2255
+ if (cmdOpts.component) {
2256
+ steps = steps.filter((s) => s.component.toLowerCase() === cmdOpts.component.toLowerCase());
2257
+ if (steps.length === 0) {
2258
+ const err = createError(
2259
+ ErrorCodes.COMPONENT_NOT_FOUND,
2260
+ `No migration steps found for ${cmdOpts.component} from v${from} to v${to}`
2261
+ );
2262
+ printError(err, opts.format);
2263
+ process.exitCode = 1;
2264
+ return;
2265
+ }
2266
+ }
2267
+ if (cmdOpts.apply) {
2268
+ formatApplyPrompt(from, to, steps, cmdOpts.apply, opts.format);
2269
+ return;
2270
+ }
2271
+ switch (opts.format) {
2272
+ case "json":
2273
+ formatJson(from, to, steps);
2274
+ break;
2275
+ case "markdown":
2276
+ formatMarkdown(from, to, steps);
2277
+ break;
2278
+ case "text":
2279
+ default:
2280
+ formatText(from, to, steps);
2281
+ break;
2282
+ }
2283
+ });
2284
+ }
2285
+
2286
+ // src/index.ts
2287
+ var CLI_VERSION = "0.1.0";
2288
+ var program = new Command();
2289
+ program.name("antd").description("CLI tool for querying antd knowledge and analyzing antd usage").option("--format <format>", "Output format: json, text, or markdown", "text").option("--version <version>", "Target antd version (e.g. 5.20.0)").option("--lang <lang>", "Output language: en or zh", "en").option("--detail", "Full information output", false);
2290
+ program.addOption(new Option("-V, --cli-version", "Output the CLI version number"));
2291
+ registerListCommand(program);
2292
+ registerInfoCommand(program);
2293
+ registerDemoCommand(program);
2294
+ registerTokenCommand(program);
2295
+ registerSemanticCommand(program);
2296
+ registerChangelogCommand(program);
2297
+ registerDoctorCommand(program);
2298
+ registerUsageCommand(program);
2299
+ registerLintCommand(program);
2300
+ registerMigrateCommand(program);
2301
+ var idx = process.argv.indexOf("-V");
2302
+ var idx2 = process.argv.indexOf("--cli-version");
2303
+ if (idx !== -1 || idx2 !== -1) {
2304
+ console.log(CLI_VERSION);
2305
+ process.exit(0);
2306
+ }
2307
+ program.parse();
2308
+ //# sourceMappingURL=index.js.map