enterprise-ui-architect-cli 1.0.0 → 2.0.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/assets/SKILL.md +98 -0
- package/assets/commands/verify-i18n.ts +213 -0
- package/assets/commands/verify-imports.ts +256 -0
- package/assets/data/anti-patterns.csv +3 -0
- package/assets/data/charts.csv +1 -1
- package/assets/data/industries.csv +5 -5
- package/assets/data/pre-delivery-checklist.csv +6 -0
- package/assets/data/review-rubric.csv +2 -2
- package/assets/scripts/search.py +21 -9
- package/assets/src/index.ts +116 -0
- package/assets/templates/base/quick-reference.md +18 -0
- package/dist/commands/verify-i18n.d.ts +7 -0
- package/dist/commands/verify-i18n.d.ts.map +1 -0
- package/dist/commands/verify-i18n.js +175 -0
- package/dist/commands/verify-i18n.js.map +1 -0
- package/dist/commands/verify-imports.d.ts +7 -0
- package/dist/commands/verify-imports.d.ts.map +1 -0
- package/dist/commands/verify-imports.js +222 -0
- package/dist/commands/verify-imports.js.map +1 -0
- package/dist/index.js +42 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/assets/scripts/search.py
CHANGED
|
@@ -52,8 +52,8 @@ def find_data_dir() -> Path:
|
|
|
52
52
|
return candidates[0]
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
def bm25_score(row: dict, query_terms: list[str], avgdl: float, k1: float = 1.5, b: float = 0.75) -> float:
|
|
56
|
-
"""
|
|
55
|
+
def bm25_score(row: dict, query_terms: list[str], term_doc_counts: dict[str, int], total_docs: int, avgdl: float, k1: float = 1.5, b: float = 0.75) -> float:
|
|
56
|
+
"""BM25 scoring with proper IDF across all row values."""
|
|
57
57
|
text = " ".join(str(v).lower() for v in row.values() if v is not None)
|
|
58
58
|
doc_len = len(text.split())
|
|
59
59
|
score = 0.0
|
|
@@ -61,9 +61,9 @@ def bm25_score(row: dict, query_terms: list[str], avgdl: float, k1: float = 1.5,
|
|
|
61
61
|
tf = text.count(term.lower())
|
|
62
62
|
if tf == 0:
|
|
63
63
|
continue
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
idf =
|
|
64
|
+
n = term_doc_counts.get(term.lower(), 1)
|
|
65
|
+
idf = max(0.0, (total_docs - n + 0.5) / (n + 0.5))
|
|
66
|
+
idf = max(0.01, idf) # Prevent negative IDF
|
|
67
67
|
tf_component = (tf * (k1 + 1)) / (tf + k1 * (1 - b + b * (doc_len / max(avgdl, 1))))
|
|
68
68
|
score += idf * tf_component
|
|
69
69
|
return score
|
|
@@ -79,11 +79,11 @@ def search_csv(data_dir: Path, domain: str, query: str, max_results: int = 10) -
|
|
|
79
79
|
if not filepath.exists():
|
|
80
80
|
return f"Error: File not found: {filepath}"
|
|
81
81
|
|
|
82
|
-
query_terms = query.split()
|
|
82
|
+
query_terms = [t.lower() for t in query.split()]
|
|
83
83
|
results = []
|
|
84
84
|
total_docs = 0
|
|
85
85
|
|
|
86
|
-
# First pass: count total docs and
|
|
86
|
+
# First pass: count total docs, avgdl, and term document frequencies
|
|
87
87
|
with open(filepath, newline="", encoding="utf-8") as f:
|
|
88
88
|
reader = csv.DictReader(f)
|
|
89
89
|
docs = list(reader)
|
|
@@ -91,9 +91,18 @@ def search_csv(data_dir: Path, domain: str, query: str, max_results: int = 10) -
|
|
|
91
91
|
total_len = sum(len(" ".join(str(v) for v in r.values() if v is not None).split()) for r in docs)
|
|
92
92
|
avgdl = total_len / max(total_docs, 1)
|
|
93
93
|
|
|
94
|
+
term_doc_counts: dict[str, int] = {}
|
|
95
|
+
for term in query_terms:
|
|
96
|
+
count = 0
|
|
97
|
+
for row in docs:
|
|
98
|
+
text = " ".join(str(v).lower() for v in row.values() if v is not None)
|
|
99
|
+
if term in text:
|
|
100
|
+
count += 1
|
|
101
|
+
term_doc_counts[term] = max(count, 1)
|
|
102
|
+
|
|
94
103
|
# Second pass: score
|
|
95
104
|
for row in docs:
|
|
96
|
-
s = bm25_score(row, query_terms, avgdl)
|
|
105
|
+
s = bm25_score(row, query_terms, term_doc_counts, total_docs, avgdl)
|
|
97
106
|
if s > 0:
|
|
98
107
|
results.append((s, row))
|
|
99
108
|
|
|
@@ -227,10 +236,13 @@ def main():
|
|
|
227
236
|
|
|
228
237
|
if args.domain == "all":
|
|
229
238
|
for domain in DOMAINS:
|
|
239
|
+
result = search_csv(data_dir, domain, args.query, args.n)
|
|
240
|
+
if "No results" in result:
|
|
241
|
+
continue
|
|
230
242
|
print(f"\n{'='*60}")
|
|
231
243
|
print(f"Domain: {domain}")
|
|
232
244
|
print(f"{'='*60}")
|
|
233
|
-
print(
|
|
245
|
+
print(result)
|
|
234
246
|
return
|
|
235
247
|
|
|
236
248
|
output = search_csv(data_dir, args.domain, args.query, args.n)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { initCommand } from "./commands/init.js";
|
|
3
|
+
import { verifyImportsCommand } from "./commands/verify-imports.js";
|
|
4
|
+
import { verifyI18nCommand } from "./commands/verify-i18n.js";
|
|
5
|
+
|
|
6
|
+
function showHelp(): void {
|
|
7
|
+
console.log(`
|
|
8
|
+
Enterprise UI Architect CLI
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
enterprise-ui init [options]
|
|
12
|
+
enterprise-ui verify-imports [options]
|
|
13
|
+
enterprise-ui verify-i18n [options]
|
|
14
|
+
|
|
15
|
+
Commands:
|
|
16
|
+
init Install skill into AI coding assistants
|
|
17
|
+
verify-imports Scan source files and report missing npm packages
|
|
18
|
+
verify-i18n Scan source files and report missing translation keys
|
|
19
|
+
|
|
20
|
+
Options:
|
|
21
|
+
--ai <assistant> Target AI assistant: cursor, claude, windsurf, copilot, codex, all (default: all)
|
|
22
|
+
--offline Use local assets without network (default: false)
|
|
23
|
+
--src <dir> Source directory to scan (default: current directory)
|
|
24
|
+
--help Show this help
|
|
25
|
+
--version Show version
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
enterprise-ui init --ai cursor
|
|
29
|
+
enterprise-ui init --ai claude --offline
|
|
30
|
+
enterprise-ui verify-imports
|
|
31
|
+
enterprise-ui verify-imports --src ./src
|
|
32
|
+
enterprise-ui verify-i18n
|
|
33
|
+
enterprise-ui verify-i18n --src ./src
|
|
34
|
+
`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parseArgs(args: string[]): {
|
|
38
|
+
command?: string;
|
|
39
|
+
ai: string;
|
|
40
|
+
offline: boolean;
|
|
41
|
+
srcDir: string;
|
|
42
|
+
help: boolean;
|
|
43
|
+
version: boolean;
|
|
44
|
+
} {
|
|
45
|
+
const result = { ai: "all", offline: false, srcDir: process.cwd(), help: false, version: false };
|
|
46
|
+
let command: string | undefined;
|
|
47
|
+
|
|
48
|
+
for (let i = 0; i < args.length; i++) {
|
|
49
|
+
const arg = args[i];
|
|
50
|
+
if (arg === "init" || arg === "verify-imports" || arg === "verify-i18n") {
|
|
51
|
+
command = arg;
|
|
52
|
+
} else if (arg === "--ai" && i + 1 < args.length) {
|
|
53
|
+
result.ai = args[++i];
|
|
54
|
+
} else if (arg === "--offline") {
|
|
55
|
+
result.offline = true;
|
|
56
|
+
} else if (arg === "--src" && i + 1 < args.length) {
|
|
57
|
+
result.srcDir = args[++i];
|
|
58
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
59
|
+
result.help = true;
|
|
60
|
+
} else if (arg === "--version" || arg === "-v") {
|
|
61
|
+
result.version = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { command, ...result };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function main(): number {
|
|
69
|
+
const args = process.argv.slice(2);
|
|
70
|
+
const parsed = parseArgs(args);
|
|
71
|
+
|
|
72
|
+
if (parsed.version) {
|
|
73
|
+
console.log("2.0.0");
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (parsed.help || args.length === 0) {
|
|
78
|
+
showHelp();
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (parsed.command === "init") {
|
|
83
|
+
try {
|
|
84
|
+
initCommand({ ai: parsed.ai, offline: parsed.offline });
|
|
85
|
+
return 0;
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.error(`Error: ${(err as Error).message}`);
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (parsed.command === "verify-imports") {
|
|
93
|
+
try {
|
|
94
|
+
verifyImportsCommand({ srcDir: parsed.srcDir });
|
|
95
|
+
return 0;
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.error(`Error: ${(err as Error).message}`);
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (parsed.command === "verify-i18n") {
|
|
103
|
+
try {
|
|
104
|
+
verifyI18nCommand({ srcDir: parsed.srcDir });
|
|
105
|
+
return 0;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error(`Error: ${(err as Error).message}`);
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.error(`Unknown command. Run --help for usage.`);
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
process.exit(main());
|
|
@@ -93,6 +93,24 @@ python scripts/search.py --query "saas" --design-system --persist --product "MyA
|
|
|
93
93
|
- [ ] Implement retry + error fallback for unstable APIs
|
|
94
94
|
- [ ] Use refetchInterval or WebSocket for real-time dashboards
|
|
95
95
|
|
|
96
|
+
## Package Import Verification
|
|
97
|
+
```bash
|
|
98
|
+
enterprise-ui verify-imports --src ./src
|
|
99
|
+
```
|
|
100
|
+
- [ ] All new imports resolve to installed packages
|
|
101
|
+
- [ ] Check package.json before adding imports
|
|
102
|
+
- [ ] Ask user before installing missing packages
|
|
103
|
+
- [ ] Post-install: run `npx tsc --noEmit`
|
|
104
|
+
|
|
105
|
+
## Translation Verification
|
|
106
|
+
```bash
|
|
107
|
+
enterprise-ui verify-i18n --src ./src
|
|
108
|
+
```
|
|
109
|
+
- [ ] All user-facing strings use `t()` — no hardcoded text
|
|
110
|
+
- [ ] Every `t("key")` exists in all `messages/*.json` files
|
|
111
|
+
- [ ] Use `useTranslations("namespace")` for page-scoped keys
|
|
112
|
+
- [ ] Avoid dynamic template literals for keys
|
|
113
|
+
|
|
96
114
|
## Pre-Delivery Checklist
|
|
97
115
|
- [ ] All interactive elements have hover states and cursor-pointer
|
|
98
116
|
- [ ] Focus states are visible and consistent
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-i18n.d.ts","sourceRoot":"","sources":["../../src/commands/verify-i18n.ts"],"names":[],"mappings":"AAGA,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAoID,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,OAAO,CAAC,aAAa,CAAM,GAAG,IAAI,CA0E5E"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { readFileSync, existsSync, readdirSync } from "fs";
|
|
2
|
+
import { resolve, dirname, join, sep, extname } from "path";
|
|
3
|
+
function findMessagesDir(startDir) {
|
|
4
|
+
let dir = resolve(startDir);
|
|
5
|
+
while (dir !== dirname(dir)) {
|
|
6
|
+
const messagesPath = join(dir, "messages");
|
|
7
|
+
if (existsSync(messagesPath))
|
|
8
|
+
return messagesPath;
|
|
9
|
+
dir = dirname(dir);
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
function getSourceFiles(dir) {
|
|
14
|
+
const results = [];
|
|
15
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const fullPath = join(dir, entry.name);
|
|
18
|
+
if (entry.isDirectory()) {
|
|
19
|
+
if (entry.name === "node_modules" ||
|
|
20
|
+
entry.name === "dist" ||
|
|
21
|
+
entry.name === "build" ||
|
|
22
|
+
entry.name === ".next" ||
|
|
23
|
+
entry.name.startsWith(".")) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
results.push(...getSourceFiles(fullPath));
|
|
27
|
+
}
|
|
28
|
+
else if (entry.isFile()) {
|
|
29
|
+
const ext = extname(entry.name);
|
|
30
|
+
if (ext === ".ts" || ext === ".tsx" || ext === ".js" || ext === ".jsx") {
|
|
31
|
+
results.push(fullPath);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return results;
|
|
36
|
+
}
|
|
37
|
+
function loadTranslations(messagesDir) {
|
|
38
|
+
const files = readdirSync(messagesDir).filter((f) => f.endsWith(".json"));
|
|
39
|
+
const translations = {};
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
const locale = file.replace(".json", "");
|
|
42
|
+
const content = readFileSync(join(messagesDir, file), "utf-8");
|
|
43
|
+
translations[locale] = JSON.parse(content);
|
|
44
|
+
}
|
|
45
|
+
return translations;
|
|
46
|
+
}
|
|
47
|
+
function getNestedValue(obj, path) {
|
|
48
|
+
const parts = path.split(".");
|
|
49
|
+
let current = obj;
|
|
50
|
+
for (const part of parts) {
|
|
51
|
+
if (current === null || current === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
if (typeof current !== "object")
|
|
54
|
+
return undefined;
|
|
55
|
+
current = current[part];
|
|
56
|
+
}
|
|
57
|
+
return current;
|
|
58
|
+
}
|
|
59
|
+
function extractNamespace(content) {
|
|
60
|
+
const match = /useTranslations\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/.exec(content);
|
|
61
|
+
return match ? match[1] : null;
|
|
62
|
+
}
|
|
63
|
+
function isInsideStringLiteral(line, matchIndex) {
|
|
64
|
+
// Check if the character before t( is inside a string literal
|
|
65
|
+
const before = line.slice(0, matchIndex);
|
|
66
|
+
let inSingle = false;
|
|
67
|
+
let inDouble = false;
|
|
68
|
+
let escaped = false;
|
|
69
|
+
for (const ch of before) {
|
|
70
|
+
if (escaped) {
|
|
71
|
+
escaped = false;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (ch === "\\") {
|
|
75
|
+
escaped = true;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (ch === '"' && !inSingle) {
|
|
79
|
+
inDouble = !inDouble;
|
|
80
|
+
}
|
|
81
|
+
else if (ch === "'" && !inDouble) {
|
|
82
|
+
inSingle = !inSingle;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return inSingle || inDouble;
|
|
86
|
+
}
|
|
87
|
+
function extractTranslationKeys(content, namespace) {
|
|
88
|
+
const lines = content.split("\n");
|
|
89
|
+
const keys = [];
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
const regex = /\bt\s*\(\s*["'`]([a-zA-Z0-9_.-]+)["'`]/g;
|
|
92
|
+
for (let i = 0; i < lines.length; i++) {
|
|
93
|
+
const line = lines[i];
|
|
94
|
+
// Skip comment-only lines
|
|
95
|
+
const codePart = line.split("//")[0];
|
|
96
|
+
if (!codePart.includes("t("))
|
|
97
|
+
continue;
|
|
98
|
+
let match;
|
|
99
|
+
while ((match = regex.exec(line)) !== null) {
|
|
100
|
+
if (isInsideStringLiteral(line, match.index))
|
|
101
|
+
continue;
|
|
102
|
+
const rawKey = match[1];
|
|
103
|
+
const key = namespace ? `${namespace}.${rawKey}` : rawKey;
|
|
104
|
+
if (!seen.has(key)) {
|
|
105
|
+
seen.add(key);
|
|
106
|
+
keys.push({ key, file: "", line: i + 1 });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return keys;
|
|
111
|
+
}
|
|
112
|
+
export function verifyI18nCommand(options = {}) {
|
|
113
|
+
const srcDir = options.srcDir || process.cwd();
|
|
114
|
+
const messagesDir = options.messagesDir || findMessagesDir(srcDir);
|
|
115
|
+
if (!messagesDir) {
|
|
116
|
+
console.error("❌ messages/ directory not found. Run this from a project root.");
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
const translations = loadTranslations(messagesDir);
|
|
120
|
+
const locales = Object.keys(translations);
|
|
121
|
+
if (locales.length === 0) {
|
|
122
|
+
console.error("❌ No translation files found in messages/ directory.");
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
const files = getSourceFiles(srcDir);
|
|
126
|
+
const allKeys = new Map();
|
|
127
|
+
for (const file of files) {
|
|
128
|
+
const content = readFileSync(file, "utf-8");
|
|
129
|
+
const namespace = extractNamespace(content);
|
|
130
|
+
const keys = extractTranslationKeys(content, namespace);
|
|
131
|
+
for (const { key, line } of keys) {
|
|
132
|
+
const list = allKeys.get(key) || [];
|
|
133
|
+
list.push({ file, line });
|
|
134
|
+
allKeys.set(key, list);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (allKeys.size === 0) {
|
|
138
|
+
console.log("ℹ️ No translation keys found in source files.");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const missingByLocale = new Map();
|
|
142
|
+
for (const [key, locations] of allKeys) {
|
|
143
|
+
for (const locale of locales) {
|
|
144
|
+
const value = getNestedValue(translations[locale], key);
|
|
145
|
+
if (value === undefined) {
|
|
146
|
+
const list = missingByLocale.get(locale) || [];
|
|
147
|
+
list.push({ key, file: locations[0].file, line: locations[0].line });
|
|
148
|
+
missingByLocale.set(locale, list);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const totalMissing = Array.from(missingByLocale.values()).reduce((sum, list) => sum + list.length, 0);
|
|
153
|
+
if (totalMissing === 0) {
|
|
154
|
+
console.log(`✅ All ${allKeys.size} translation key(s) exist in every locale (${locales.join(", ")}).`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
console.log(`⚠️ Found ${totalMissing} missing translation key(s):\n`);
|
|
158
|
+
for (const [locale, missing] of missingByLocale) {
|
|
159
|
+
if (missing.length === 0)
|
|
160
|
+
continue;
|
|
161
|
+
console.log(` 🌐 ${locale}.json (${missing.length} missing):`);
|
|
162
|
+
for (const { key, file, line } of missing.slice(0, 10)) {
|
|
163
|
+
const relPath = file.replace(srcDir + sep, "").replace(/^\//, "");
|
|
164
|
+
console.log(` - ${key}`);
|
|
165
|
+
console.log(` used in: ${relPath}:${line}`);
|
|
166
|
+
}
|
|
167
|
+
if (missing.length > 10) {
|
|
168
|
+
console.log(` ... and ${missing.length - 10} more`);
|
|
169
|
+
}
|
|
170
|
+
console.log("");
|
|
171
|
+
}
|
|
172
|
+
console.log(`Add the missing keys to all ${locales.length} locale files.\n`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=verify-i18n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-i18n.js","sourceRoot":"","sources":["../../src/commands/verify-i18n.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-imports.d.ts","sourceRoot":"","sources":["../../src/commands/verify-imports.ts"],"names":[],"mappings":"AAGA,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;CACzB;AA8KD,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,OAAO,CAAC,aAAa,CAAM,GAAG,IAAI,CA2E/E"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { readFileSync, existsSync, readdirSync } from "fs";
|
|
2
|
+
import { resolve, dirname, join, sep, extname } from "path";
|
|
3
|
+
const NODE_BUILTINS = new Set([
|
|
4
|
+
"assert", "async_hooks", "buffer", "child_process", "cluster", "console",
|
|
5
|
+
"constants", "crypto", "dgram", "diagnostics_channel", "dns", "domain",
|
|
6
|
+
"events", "fs", "http", "http2", "https", "inspector", "module", "net",
|
|
7
|
+
"os", "path", "perf_hooks", "process", "punycode", "querystring", "readline",
|
|
8
|
+
"repl", "stream", "string_decoder", "sys", "timers", "timers/promises",
|
|
9
|
+
"tls", "trace_events", "tty", "url", "util", "v8", "vm", "wasi", "worker_threads",
|
|
10
|
+
"zlib", "node:test",
|
|
11
|
+
]);
|
|
12
|
+
function findPackageJson(startDir) {
|
|
13
|
+
let dir = resolve(startDir);
|
|
14
|
+
while (dir !== dirname(dir)) {
|
|
15
|
+
const pkgPath = join(dir, "package.json");
|
|
16
|
+
if (existsSync(pkgPath))
|
|
17
|
+
return pkgPath;
|
|
18
|
+
dir = dirname(dir);
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
function findTsConfig(startDir) {
|
|
23
|
+
let dir = resolve(startDir);
|
|
24
|
+
while (dir !== dirname(dir)) {
|
|
25
|
+
const tsPath = join(dir, "tsconfig.json");
|
|
26
|
+
if (existsSync(tsPath))
|
|
27
|
+
return tsPath;
|
|
28
|
+
dir = dirname(dir);
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
function getInstalledPackages(packageJsonPath) {
|
|
33
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
34
|
+
const deps = Object.keys(pkg.dependencies || {});
|
|
35
|
+
const devDeps = Object.keys(pkg.devDependencies || {});
|
|
36
|
+
const peerDeps = Object.keys(pkg.peerDependencies || {});
|
|
37
|
+
const optionalDeps = Object.keys(pkg.optionalDependencies || {});
|
|
38
|
+
return new Set([...deps, ...devDeps, ...peerDeps, ...optionalDeps]);
|
|
39
|
+
}
|
|
40
|
+
function getPathAliases(tsConfigPath) {
|
|
41
|
+
try {
|
|
42
|
+
const raw = readFileSync(tsConfigPath, "utf-8");
|
|
43
|
+
// Extract path aliases via regex — tolerant to comments and trailing commas
|
|
44
|
+
const aliases = [];
|
|
45
|
+
// Match patterns like "@core/*" or "@/components/*" inside "paths": { ... }
|
|
46
|
+
const pathsMatch = raw.match(/"paths"\s*:\s*\{([\s\S]*?)\}/);
|
|
47
|
+
if (pathsMatch) {
|
|
48
|
+
const pathsBlock = pathsMatch[1];
|
|
49
|
+
const keyRegex = /"(@[^"]+)"\s*:/g;
|
|
50
|
+
let m;
|
|
51
|
+
while ((m = keyRegex.exec(pathsBlock)) !== null) {
|
|
52
|
+
aliases.push(m[1]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return aliases.length > 0 ? aliases : ["@/*"];
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return ["@/*"];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function isPathAlias(source, aliases) {
|
|
62
|
+
for (const alias of aliases) {
|
|
63
|
+
const prefix = alias.replace(/\/\*$/, "");
|
|
64
|
+
if (alias.endsWith("/*")) {
|
|
65
|
+
if (source === prefix || source.startsWith(prefix + "/")) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
if (source === alias) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
function getSourceFiles(dir) {
|
|
78
|
+
const results = [];
|
|
79
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const fullPath = join(dir, entry.name);
|
|
82
|
+
if (entry.isDirectory()) {
|
|
83
|
+
if (entry.name === "node_modules" ||
|
|
84
|
+
entry.name === "dist" ||
|
|
85
|
+
entry.name === "build" ||
|
|
86
|
+
entry.name === ".next" ||
|
|
87
|
+
entry.name.startsWith(".")) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
results.push(...getSourceFiles(fullPath));
|
|
91
|
+
}
|
|
92
|
+
else if (entry.isFile()) {
|
|
93
|
+
const ext = extname(entry.name);
|
|
94
|
+
if (ext === ".ts" || ext === ".tsx" || ext === ".js" || ext === ".jsx") {
|
|
95
|
+
results.push(fullPath);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return results;
|
|
100
|
+
}
|
|
101
|
+
function extractImports(filePath) {
|
|
102
|
+
const content = readFileSync(filePath, "utf-8");
|
|
103
|
+
const lines = content.split("\n");
|
|
104
|
+
const imports = [];
|
|
105
|
+
// Matches single-line imports: import ... from "..."
|
|
106
|
+
const singleLineRegex = /^(?:import\s+.*?from\s+|import\s*\(|require\s*\()["']([^"';]+)["'];?/;
|
|
107
|
+
// Matches multi-line import end: from "..."
|
|
108
|
+
const multiLineEndRegex = /from\s+["']([^"';]+)["'];?/;
|
|
109
|
+
for (let i = 0; i < lines.length; i++) {
|
|
110
|
+
const line = lines[i].trim();
|
|
111
|
+
const singleMatch = singleLineRegex.exec(line);
|
|
112
|
+
if (singleMatch) {
|
|
113
|
+
imports.push({ path: filePath, line: i + 1, source: singleMatch[1] });
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
// Multi-line import: starts with "import" but no "from" on same line
|
|
117
|
+
if (line.startsWith("import") && !line.includes("from") && !line.includes("(")) {
|
|
118
|
+
// Look ahead up to 5 lines for "from '...'"
|
|
119
|
+
for (let j = i + 1; j < Math.min(i + 6, lines.length); j++) {
|
|
120
|
+
const nextLine = lines[j].trim();
|
|
121
|
+
const multiMatch = multiLineEndRegex.exec(nextLine);
|
|
122
|
+
if (multiMatch) {
|
|
123
|
+
imports.push({ path: filePath, line: j + 1, source: multiMatch[1] });
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
if (nextLine.endsWith(";"))
|
|
127
|
+
break; // End of import without from
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return imports;
|
|
132
|
+
}
|
|
133
|
+
function resolvePackageName(source) {
|
|
134
|
+
if (source.startsWith(".") || source.startsWith("/"))
|
|
135
|
+
return null;
|
|
136
|
+
if (source.startsWith("node:"))
|
|
137
|
+
return null;
|
|
138
|
+
if (source.startsWith("@/"))
|
|
139
|
+
return null;
|
|
140
|
+
if (source.startsWith("@")) {
|
|
141
|
+
const parts = source.split("/");
|
|
142
|
+
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : null;
|
|
143
|
+
}
|
|
144
|
+
const idx = source.indexOf("/");
|
|
145
|
+
return idx > 0 ? source.slice(0, idx) : source;
|
|
146
|
+
}
|
|
147
|
+
function isNodeBuiltin(pkg) {
|
|
148
|
+
return NODE_BUILTINS.has(pkg) || NODE_BUILTINS.has(`node:${pkg}`);
|
|
149
|
+
}
|
|
150
|
+
function detectPackageManager(lockFiles) {
|
|
151
|
+
if (lockFiles.some((f) => f.endsWith("bun.lockb")))
|
|
152
|
+
return "bun";
|
|
153
|
+
if (lockFiles.some((f) => f.endsWith("pnpm-lock.yaml")))
|
|
154
|
+
return "pnpm";
|
|
155
|
+
if (lockFiles.some((f) => f.endsWith("yarn.lock")))
|
|
156
|
+
return "yarn";
|
|
157
|
+
if (lockFiles.some((f) => f.endsWith("package-lock.json")))
|
|
158
|
+
return "npm";
|
|
159
|
+
return "unknown";
|
|
160
|
+
}
|
|
161
|
+
export function verifyImportsCommand(options = {}) {
|
|
162
|
+
const srcDir = options.srcDir || process.cwd();
|
|
163
|
+
const packageJsonPath = options.packageJsonPath || findPackageJson(srcDir);
|
|
164
|
+
if (!packageJsonPath) {
|
|
165
|
+
console.error("❌ package.json not found. Run this from a project root.");
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
const installed = getInstalledPackages(packageJsonPath);
|
|
169
|
+
const projectRoot = dirname(packageJsonPath);
|
|
170
|
+
const tsConfigPath = findTsConfig(srcDir);
|
|
171
|
+
const pathAliases = tsConfigPath ? getPathAliases(tsConfigPath) : [];
|
|
172
|
+
const lockFiles = ["package-lock.json", "yarn.lock", "pnpm-lock.yaml", "bun.lockb"].map((f) => join(projectRoot, f));
|
|
173
|
+
const existingLocks = lockFiles.filter((f) => existsSync(f));
|
|
174
|
+
const pkgManager = detectPackageManager(existingLocks);
|
|
175
|
+
const files = getSourceFiles(srcDir);
|
|
176
|
+
const allImports = [];
|
|
177
|
+
for (const file of files) {
|
|
178
|
+
allImports.push(...extractImports(file));
|
|
179
|
+
}
|
|
180
|
+
const missing = new Map();
|
|
181
|
+
for (const imp of allImports) {
|
|
182
|
+
if (isPathAlias(imp.source, pathAliases))
|
|
183
|
+
continue;
|
|
184
|
+
const pkg = resolvePackageName(imp.source);
|
|
185
|
+
if (!pkg)
|
|
186
|
+
continue;
|
|
187
|
+
if (isNodeBuiltin(pkg))
|
|
188
|
+
continue;
|
|
189
|
+
if (installed.has(pkg))
|
|
190
|
+
continue;
|
|
191
|
+
const list = missing.get(pkg) || [];
|
|
192
|
+
list.push(imp);
|
|
193
|
+
missing.set(pkg, list);
|
|
194
|
+
}
|
|
195
|
+
if (missing.size === 0) {
|
|
196
|
+
console.log(`✅ All imports resolve to installed packages (${allImports.length} imports checked).`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
console.log(`⚠️ Found ${missing.size} missing package(s):\n`);
|
|
200
|
+
for (const [pkg, imports] of missing) {
|
|
201
|
+
console.log(` 📦 ${pkg}`);
|
|
202
|
+
console.log(` Used in:`);
|
|
203
|
+
for (const imp of imports.slice(0, 3)) {
|
|
204
|
+
const relPath = imp.path.replace(projectRoot + sep, "").replace(/^\//, "");
|
|
205
|
+
console.log(` - ${relPath}:${imp.line} → import from "${imp.source}"`);
|
|
206
|
+
}
|
|
207
|
+
if (imports.length > 3) {
|
|
208
|
+
console.log(` ... and ${imports.length - 3} more`);
|
|
209
|
+
}
|
|
210
|
+
const installCmd = pkgManager === "bun"
|
|
211
|
+
? `bun add ${pkg}`
|
|
212
|
+
: pkgManager === "pnpm"
|
|
213
|
+
? `pnpm add ${pkg}`
|
|
214
|
+
: pkgManager === "yarn"
|
|
215
|
+
? `yarn add ${pkg}`
|
|
216
|
+
: `npm install ${pkg}`;
|
|
217
|
+
console.log(` Install: ${installCmd}\n`);
|
|
218
|
+
}
|
|
219
|
+
console.log(`Run the install command(s) above, then re-run this check.\n`);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=verify-imports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-imports.js","sourceRoot":"","sources":["../../src/commands/verify-imports.ts"],"names":[],"mappings":"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"}
|