uilint 0.2.165 → 0.2.166

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.
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/profile.ts
4
+ import { existsSync, readFileSync } from "fs";
5
+ import { isAbsolute, join, resolve } from "path";
6
+ import pc from "picocolors";
7
+ function resolveProfileDir(profileDir) {
8
+ const dir = profileDir || process.env.UILINT_PROFILE_DIR || ".uilint/profile";
9
+ return isAbsolute(dir) ? dir : resolve(process.cwd(), dir);
10
+ }
11
+ function readLatestProfile(profileDir) {
12
+ const latestPath = join(profileDir, "latest.json");
13
+ if (!existsSync(latestPath)) {
14
+ throw new Error(`No profile found at ${latestPath}`);
15
+ }
16
+ return JSON.parse(readFileSync(latestPath, "utf-8"));
17
+ }
18
+ function formatMs(value) {
19
+ return `${value.toFixed(value >= 10 ? 1 : 3)}ms`;
20
+ }
21
+ function pad(value, width) {
22
+ return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
23
+ }
24
+ function printRuleTable(title, rules) {
25
+ console.log(pc.bold(title));
26
+ if (rules.length === 0) {
27
+ console.log(pc.dim(" No rule timings recorded."));
28
+ return;
29
+ }
30
+ console.log(
31
+ pc.dim(
32
+ ` ${pad("rule", 34)} ${pad("total", 10)} ${pad("avg/file", 10)} ${pad("p95", 10)} ${pad("p99", 10)} ${pad("files", 7)} reports`
33
+ )
34
+ );
35
+ for (const rule of rules) {
36
+ console.log(
37
+ ` ${pad(rule.ruleId, 34)} ${pad(formatMs(rule.totalMs), 10)} ${pad(formatMs(rule.avgFileMs), 10)} ${pad(formatMs(rule.p95FileMs), 10)} ${pad(formatMs(rule.p99FileMs), 10)} ${pad(String(rule.files), 7)} ${rule.reports}`
38
+ );
39
+ }
40
+ }
41
+ function printOutliers(outliers) {
42
+ console.log(pc.bold("Top Outliers"));
43
+ if (outliers.length === 0) {
44
+ console.log(pc.dim(" No outliers met the configured threshold."));
45
+ return;
46
+ }
47
+ console.log(
48
+ pc.dim(
49
+ ` ${pad("rule", 30)} ${pad("total", 10)} ${pad("listeners", 10)} file`
50
+ )
51
+ );
52
+ for (const outlier of outliers) {
53
+ console.log(
54
+ ` ${pad(outlier.ruleId, 30)} ${pad(formatMs(outlier.totalMs), 10)} ${pad(String(outlier.listenerCalls), 10)} ${outlier.filePath}`
55
+ );
56
+ }
57
+ }
58
+ async function profile(options) {
59
+ const profileDir = resolveProfileDir(options.profileDir);
60
+ const session = readLatestProfile(profileDir);
61
+ const limit = Math.max(1, options.limit ?? 10);
62
+ if (options.json) {
63
+ console.log(JSON.stringify(session, null, 2));
64
+ return;
65
+ }
66
+ console.log(pc.bold("UILint Rule Profile"));
67
+ console.log(` Generated: ${session.generatedAt}`);
68
+ console.log(` CWD: ${session.cwd}`);
69
+ console.log(` Node: ${session.nodeVersion}`);
70
+ console.log(` Duration: ${formatMs(session.durationMs)}`);
71
+ console.log(` Files: ${session.fileCount}`);
72
+ console.log(` Enabled rules: ${session.enabledRuleCount}`);
73
+ console.log(` Profile source: ${join(profileDir, "latest.json")}`);
74
+ console.log();
75
+ printRuleTable(
76
+ "Slowest Rules By Total Time",
77
+ [...session.rules].sort((a, b) => b.totalMs - a.totalMs).slice(0, limit)
78
+ );
79
+ console.log();
80
+ printRuleTable(
81
+ "Slowest Rules By Average File Time",
82
+ [...session.rules].sort((a, b) => b.avgFileMs - a.avgFileMs).slice(0, limit)
83
+ );
84
+ console.log();
85
+ printOutliers(session.outliers.slice(0, limit));
86
+ }
87
+ export {
88
+ profile
89
+ };
90
+ //# sourceMappingURL=profile-IRI5WLW7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/profile.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"fs\";\nimport { isAbsolute, join, resolve } from \"path\";\nimport pc from \"picocolors\";\n\ninterface RuleProfileListenerSummary {\n selector: string;\n totalMs: number;\n calls: number;\n}\n\ninterface RuleProfileSummary {\n ruleId: string;\n files: number;\n reports: number;\n setupMs: number;\n listenerMs: number;\n totalMs: number;\n listenerCalls: number;\n avgFileMs: number;\n p95FileMs: number;\n p99FileMs: number;\n maxFileMs: number;\n listeners: RuleProfileListenerSummary[];\n}\n\ninterface RuleProfileOutlier {\n ruleId: string;\n filePath: string;\n totalMs: number;\n setupMs: number;\n listenerMs: number;\n listenerCalls: number;\n reports: number;\n}\n\ninterface RuleProfileSession {\n version: number;\n generatedAt: string;\n durationMs: number;\n cwd: string;\n nodeVersion: string;\n fileCount: number;\n enabledRuleCount: number;\n rules: RuleProfileSummary[];\n outliers: RuleProfileOutlier[];\n}\n\nexport interface ProfileCommandOptions {\n profileDir?: string;\n json?: boolean;\n limit?: number;\n}\n\nfunction resolveProfileDir(profileDir?: string): string {\n const dir = profileDir || process.env.UILINT_PROFILE_DIR || \".uilint/profile\";\n return isAbsolute(dir) ? dir : resolve(process.cwd(), dir);\n}\n\nfunction readLatestProfile(profileDir: string): RuleProfileSession {\n const latestPath = join(profileDir, \"latest.json\");\n if (!existsSync(latestPath)) {\n throw new Error(`No profile found at ${latestPath}`);\n }\n\n return JSON.parse(readFileSync(latestPath, \"utf-8\")) as RuleProfileSession;\n}\n\nfunction formatMs(value: number): string {\n return `${value.toFixed(value >= 10 ? 1 : 3)}ms`;\n}\n\nfunction pad(value: string, width: number): string {\n return value.length >= width ? value : `${value}${\" \".repeat(width - value.length)}`;\n}\n\nfunction printRuleTable(title: string, rules: RuleProfileSummary[]): void {\n console.log(pc.bold(title));\n if (rules.length === 0) {\n console.log(pc.dim(\" No rule timings recorded.\"));\n return;\n }\n\n console.log(\n pc.dim(\n ` ${pad(\"rule\", 34)} ${pad(\"total\", 10)} ${pad(\"avg/file\", 10)} ${pad(\"p95\", 10)} ${pad(\"p99\", 10)} ${pad(\"files\", 7)} reports`\n )\n );\n\n for (const rule of rules) {\n console.log(\n ` ${pad(rule.ruleId, 34)} ${pad(formatMs(rule.totalMs), 10)} ${pad(formatMs(rule.avgFileMs), 10)} ${pad(formatMs(rule.p95FileMs), 10)} ${pad(formatMs(rule.p99FileMs), 10)} ${pad(String(rule.files), 7)} ${rule.reports}`\n );\n }\n}\n\nfunction printOutliers(outliers: RuleProfileOutlier[]): void {\n console.log(pc.bold(\"Top Outliers\"));\n if (outliers.length === 0) {\n console.log(pc.dim(\" No outliers met the configured threshold.\"));\n return;\n }\n\n console.log(\n pc.dim(\n ` ${pad(\"rule\", 30)} ${pad(\"total\", 10)} ${pad(\"listeners\", 10)} file`\n )\n );\n\n for (const outlier of outliers) {\n console.log(\n ` ${pad(outlier.ruleId, 30)} ${pad(formatMs(outlier.totalMs), 10)} ${pad(String(outlier.listenerCalls), 10)} ${outlier.filePath}`\n );\n }\n}\n\nexport async function profile(options: ProfileCommandOptions): Promise<void> {\n const profileDir = resolveProfileDir(options.profileDir);\n const session = readLatestProfile(profileDir);\n const limit = Math.max(1, options.limit ?? 10);\n\n if (options.json) {\n console.log(JSON.stringify(session, null, 2));\n return;\n }\n\n console.log(pc.bold(\"UILint Rule Profile\"));\n console.log(` Generated: ${session.generatedAt}`);\n console.log(` CWD: ${session.cwd}`);\n console.log(` Node: ${session.nodeVersion}`);\n console.log(` Duration: ${formatMs(session.durationMs)}`);\n console.log(` Files: ${session.fileCount}`);\n console.log(` Enabled rules: ${session.enabledRuleCount}`);\n console.log(` Profile source: ${join(profileDir, \"latest.json\")}`);\n console.log();\n\n printRuleTable(\n \"Slowest Rules By Total Time\",\n [...session.rules].sort((a, b) => b.totalMs - a.totalMs).slice(0, limit)\n );\n console.log();\n printRuleTable(\n \"Slowest Rules By Average File Time\",\n [...session.rules]\n .sort((a, b) => b.avgFileMs - a.avgFileMs)\n .slice(0, limit)\n );\n console.log();\n printOutliers(session.outliers.slice(0, limit));\n}\n"],"mappings":";;;AAAA,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY,MAAM,eAAe;AAC1C,OAAO,QAAQ;AAmDf,SAAS,kBAAkB,YAA6B;AACtD,QAAM,MAAM,cAAc,QAAQ,IAAI,sBAAsB;AAC5D,SAAO,WAAW,GAAG,IAAI,MAAM,QAAQ,QAAQ,IAAI,GAAG,GAAG;AAC3D;AAEA,SAAS,kBAAkB,YAAwC;AACjE,QAAM,aAAa,KAAK,YAAY,aAAa;AACjD,MAAI,CAAC,WAAW,UAAU,GAAG;AAC3B,UAAM,IAAI,MAAM,uBAAuB,UAAU,EAAE;AAAA,EACrD;AAEA,SAAO,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AACrD;AAEA,SAAS,SAAS,OAAuB;AACvC,SAAO,GAAG,MAAM,QAAQ,SAAS,KAAK,IAAI,CAAC,CAAC;AAC9C;AAEA,SAAS,IAAI,OAAe,OAAuB;AACjD,SAAO,MAAM,UAAU,QAAQ,QAAQ,GAAG,KAAK,GAAG,IAAI,OAAO,QAAQ,MAAM,MAAM,CAAC;AACpF;AAEA,SAAS,eAAe,OAAe,OAAmC;AACxE,UAAQ,IAAI,GAAG,KAAK,KAAK,CAAC;AAC1B,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,IAAI,GAAG,IAAI,6BAA6B,CAAC;AACjD;AAAA,EACF;AAEA,UAAQ;AAAA,IACN,GAAG;AAAA,MACD,KAAK,IAAI,QAAQ,EAAE,CAAC,IAAI,IAAI,SAAS,EAAE,CAAC,IAAI,IAAI,YAAY,EAAE,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC,IAAI,IAAI,SAAS,CAAC,CAAC;AAAA,IACxH;AAAA,EACF;AAEA,aAAW,QAAQ,OAAO;AACxB,YAAQ;AAAA,MACN,KAAK,IAAI,KAAK,QAAQ,EAAE,CAAC,IAAI,IAAI,SAAS,KAAK,OAAO,GAAG,EAAE,CAAC,IAAI,IAAI,SAAS,KAAK,SAAS,GAAG,EAAE,CAAC,IAAI,IAAI,SAAS,KAAK,SAAS,GAAG,EAAE,CAAC,IAAI,IAAI,SAAS,KAAK,SAAS,GAAG,EAAE,CAAC,IAAI,IAAI,OAAO,KAAK,KAAK,GAAG,CAAC,CAAC,IAAI,KAAK,OAAO;AAAA,IAC3N;AAAA,EACF;AACF;AAEA,SAAS,cAAc,UAAsC;AAC3D,UAAQ,IAAI,GAAG,KAAK,cAAc,CAAC;AACnC,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,IAAI,GAAG,IAAI,6CAA6C,CAAC;AACjE;AAAA,EACF;AAEA,UAAQ;AAAA,IACN,GAAG;AAAA,MACD,KAAK,IAAI,QAAQ,EAAE,CAAC,IAAI,IAAI,SAAS,EAAE,CAAC,IAAI,IAAI,aAAa,EAAE,CAAC;AAAA,IAClE;AAAA,EACF;AAEA,aAAW,WAAW,UAAU;AAC9B,YAAQ;AAAA,MACN,KAAK,IAAI,QAAQ,QAAQ,EAAE,CAAC,IAAI,IAAI,SAAS,QAAQ,OAAO,GAAG,EAAE,CAAC,IAAI,IAAI,OAAO,QAAQ,aAAa,GAAG,EAAE,CAAC,IAAI,QAAQ,QAAQ;AAAA,IAClI;AAAA,EACF;AACF;AAEA,eAAsB,QAAQ,SAA+C;AAC3E,QAAM,aAAa,kBAAkB,QAAQ,UAAU;AACvD,QAAM,UAAU,kBAAkB,UAAU;AAC5C,QAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,SAAS,EAAE;AAE7C,MAAI,QAAQ,MAAM;AAChB,YAAQ,IAAI,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAC5C;AAAA,EACF;AAEA,UAAQ,IAAI,GAAG,KAAK,qBAAqB,CAAC;AAC1C,UAAQ,IAAI,sBAAsB,QAAQ,WAAW,EAAE;AACvD,UAAQ,IAAI,sBAAsB,QAAQ,GAAG,EAAE;AAC/C,UAAQ,IAAI,sBAAsB,QAAQ,WAAW,EAAE;AACvD,UAAQ,IAAI,sBAAsB,SAAS,QAAQ,UAAU,CAAC,EAAE;AAChE,UAAQ,IAAI,sBAAsB,QAAQ,SAAS,EAAE;AACrD,UAAQ,IAAI,sBAAsB,QAAQ,gBAAgB,EAAE;AAC5D,UAAQ,IAAI,sBAAsB,KAAK,YAAY,aAAa,CAAC,EAAE;AACnE,UAAQ,IAAI;AAEZ;AAAA,IACE;AAAA,IACA,CAAC,GAAG,QAAQ,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK;AAAA,EACzE;AACA,UAAQ,IAAI;AACZ;AAAA,IACE;AAAA,IACA,CAAC,GAAG,QAAQ,KAAK,EACd,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS,EACxC,MAAM,GAAG,KAAK;AAAA,EACnB;AACA,UAAQ,IAAI;AACZ,gBAAc,QAAQ,SAAS,MAAM,GAAG,KAAK,CAAC;AAChD;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uilint",
3
- "version": "0.2.165",
3
+ "version": "0.2.166",
4
4
  "description": "CLI for UILint - AI-powered UI consistency checking",
5
5
  "author": "Peter Suggate",
6
6
  "repository": {
@@ -49,15 +49,15 @@
49
49
  "react": "^19.2.3",
50
50
  "typescript": "^5.9.3",
51
51
  "ws": "^8.19.0",
52
- "uilint-core": "0.2.165",
53
- "uilint-eslint": "0.2.165",
54
- "uilint-duplicates": "0.2.165"
52
+ "uilint-core": "0.2.166",
53
+ "uilint-duplicates": "0.2.166",
54
+ "uilint-eslint": "0.2.166"
55
55
  },
56
56
  "peerDependencies": {
57
- "uilint-vision": "0.2.165",
58
- "uilint-duplicates": "0.2.165",
59
- "uilint-coverage": "0.2.163",
60
- "uilint-semantic": "0.2.162"
57
+ "uilint-vision": "0.2.166",
58
+ "uilint-semantic": "0.2.163",
59
+ "uilint-coverage": "0.2.164",
60
+ "uilint-duplicates": "0.2.166"
61
61
  },
62
62
  "peerDependenciesMeta": {
63
63
  "uilint-vision": {
@@ -87,7 +87,7 @@
87
87
  "ink-testing-library": "^4.0.0",
88
88
  "tsup": "^8.5.1",
89
89
  "vitest": "^4.0.17",
90
- "uilint-react": "0.2.165"
90
+ "uilint-react": "0.2.166"
91
91
  },
92
92
  "keywords": [
93
93
  "cli",