@wot-ui/cli 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -24,6 +24,8 @@ npm install -g @wot-ui/cli
24
24
 
25
25
  安装完成后可直接使用 `wot` 命令。
26
26
 
27
+ `wot` 在交互式终端启动时会自动检查 `@wot-ui/cli` 是否有新版本。检查结果最多缓存 24 小时,提示只写入 stderr,不会污染 `--format json` 的 stdout;CI 和非交互式环境会自动跳过。`wot mcp` 不会在启动时输出更新提示,MCP 客户端可通过 `wot_status` tool 查询 CLI 更新状态。若需要关闭检查,可设置 `WOT_DISABLE_UPDATE_CHECK=1` 或 `NO_UPDATE_NOTIFIER=1`。
28
+
27
29
  如果你在仓库内本地调试,推荐直接运行源码入口,而不是依赖全局命令:
28
30
 
29
31
  ```bash
@@ -98,13 +100,18 @@ wot list --format json --version latest
98
100
 
99
101
  当前 MCP Server 提供以下 tools:
100
102
 
101
- - `wot_list`
102
- - `wot_info`
103
- - `wot_doc`
104
- - `wot_demo`
105
- - `wot_token`
106
- - `wot_changelog`
107
- - `wot_lint`
103
+ | Tool | 功能 | 主要参数 |
104
+ | --- | --- | --- |
105
+ | `wot_status` | 查看 MCP Server 与 `@wot-ui/cli` 状态,包括当前版本、是否有 CLI 更新及更新命令。 | 无 |
106
+ | `wot_list` | 列出当前离线知识库中的 wot-ui 组件元数据,适合在生成页面前发现可用组件。 | `version` |
107
+ | `wot_info` | 查询单个组件的 props、events、slots、CSS 变量等结构化信息。 | `component`, `version` |
108
+ | `wot_doc` | 获取单个组件的完整 markdown 文档,适合需要阅读用法细节或限制说明时调用。 | `component`, `version` |
109
+ | `wot_demo` | 获取组件 demo 列表,或按 demo 名称获取指定示例源码。 | `component`, `demo`, `version` |
110
+ | `wot_token` | 查询组件 CSS 变量;不传组件名时返回所有组件的 CSS 变量摘要。 | `component`, `version` |
111
+ | `wot_changelog` | 查询 wot-ui v2 离线数据中的更新记录,可按版本或组件过滤。 | `version`, `component` |
112
+ | `wot_lint` | 扫描本地项目中的 wot-ui 使用问题,例如未知组件、空按钮等规则。 | `dir`, `version` |
113
+
114
+ 其中 `version` 支持与 CLI 一致的写法,例如 `2.0`、`2.0.4`、`latest`;不传时会按项目依赖或离线数据自动解析。
108
115
 
109
116
  ## 数据来源
110
117
 
Binary file
Binary file
package/data/v2.json.gz CHANGED
Binary file
@@ -8,6 +8,8 @@
8
8
  "latest": "2.0.4"
9
9
  },
10
10
  "v2": {
11
- "2.0": "2.0.8"
11
+ "2.0": "2.0.8",
12
+ "2.1": "2.1.0",
13
+ "2.2": "2.2.0"
12
14
  }
13
15
  }
Binary file
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as detectVersion, c as version, i as listComponents, n as lintProject, o as resolveVersion, r as findComponent, s as loadMetadataFile, t as analyzeUsage } from "./scanner-CsY3-bVr.mjs";
2
+ import { a as findComponent, c as formatStatusLabel, d as resolveVersion, f as loadMetadataFile, i as lintProject, l as writeStderrLine, m as version, o as listComponents, p as name, r as analyzeUsage, s as formatLogMessage, t as checkForCliUpdate, u as detectVersion } from "./update-check-cp6uTpce.mjs";
3
3
  import process from "node:process";
4
4
  import { Command } from "commander";
5
5
  import { existsSync, readFileSync } from "node:fs";
@@ -24,7 +24,7 @@ function addQueryOptions(command) {
24
24
  function normalizeQueryOptions(options) {
25
25
  const format = options.format === "json" || options.format === "markdown" ? options.format : "text";
26
26
  const info = detectVersion(typeof options.version === "string" ? options.version : void 0);
27
- if (info.source === "fallback") process.stderr.write(`[wot] version not detected in project, falling back to ${info.version}\n`);
27
+ if (info.source === "fallback") writeStderrLine(formatLogMessage("warn", `Version not detected in project, falling back to ${info.version}`));
28
28
  return {
29
29
  format,
30
30
  version: info.version
@@ -35,7 +35,7 @@ function printError(message, format) {
35
35
  error: true,
36
36
  message
37
37
  });
38
- else console.error(message);
38
+ else console.error(formatLogMessage("error", message));
39
39
  }
40
40
  function getComponentLabel(component) {
41
41
  return `${component.name} ${component.nameZh}`;
@@ -164,21 +164,21 @@ function readPackageJson(dir) {
164
164
  if (!existsSync(packageJsonPath)) return void 0;
165
165
  return JSON.parse(readFileSync(packageJsonPath, "utf8"));
166
166
  }
167
- function getDependencyVersion(pkg, name) {
168
- return pkg?.dependencies?.[name] ?? pkg?.devDependencies?.[name];
167
+ function getDependencyVersion(pkg, name$1) {
168
+ return pkg?.dependencies?.[name$1] ?? pkg?.devDependencies?.[name$1];
169
169
  }
170
170
  function detectWotDependency(pkg) {
171
171
  const version$1 = getDependencyVersion(pkg, "@wot-ui/ui");
172
172
  return version$1 ? `@wot-ui/ui@${version$1}` : void 0;
173
173
  }
174
- function createCheck(name, status, message, suggestion) {
174
+ function createCheck(name$1, status, message, suggestion) {
175
175
  if (status === "pass") return {
176
- name,
176
+ name: name$1,
177
177
  status,
178
178
  message
179
179
  };
180
180
  return {
181
- name,
181
+ name: name$1,
182
182
  status,
183
183
  severity: status === "fail" ? "error" : "warning",
184
184
  message,
@@ -228,7 +228,7 @@ function registerDoctorCommand(program) {
228
228
  writeJson(report);
229
229
  return;
230
230
  }
231
- writeLine([`Directory: ${report.dir}`, ...report.checks.map((check) => `${check.status.toUpperCase()} ${check.name}: ${check.message}${check.suggestion ? ` | ${check.suggestion}` : ""}`)].join("\n"));
231
+ writeLine([`Directory: ${report.dir}`, ...report.checks.map((check) => `${formatStatusLabel(check.status)} ${check.name}: ${check.message}${check.suggestion ? ` | ${check.suggestion}` : ""}`)].join("\n"));
232
232
  } catch (error) {
233
233
  printError(error instanceof Error ? error.message : "Failed to diagnose project", query.format);
234
234
  process.exitCode = 1;
@@ -293,7 +293,7 @@ function registerLintCommand(program) {
293
293
  writeLine(`No lint issues found. Scanned files: ${report.scannedFiles}`);
294
294
  return;
295
295
  }
296
- writeLine([`Scanned files: ${report.scannedFiles}`, ...report.issues.map((issue) => `${issue.severity.toUpperCase()} ${issue.file}:${issue.line} [${issue.rule}] ${issue.message}`)].join("\n"));
296
+ writeLine([`Scanned files: ${report.scannedFiles}`, ...report.issues.map((issue) => `${formatStatusLabel(issue.severity)} ${issue.file}:${issue.line} [${issue.rule}] ${issue.message}`)].join("\n"));
297
297
  } catch (error) {
298
298
  printError(error instanceof Error ? error.message : "Failed to lint project", query.format);
299
299
  process.exitCode = 1;
@@ -324,7 +324,7 @@ function registerListCommand(program) {
324
324
  //#region src/commands/mcp.ts
325
325
  function registerMcpCommand(program) {
326
326
  program.command("mcp").description("Start the wot-ui MCP server").action(async () => {
327
- const { startMcpServer } = await import("./server-zoFZTrLt.mjs");
327
+ const { startMcpServer } = await import("./server-CbSmyhFy.mjs");
328
328
  await startMcpServer();
329
329
  });
330
330
  }
@@ -411,6 +411,11 @@ function createCliProgram() {
411
411
 
412
412
  //#endregion
413
413
  //#region src/index.ts
414
+ checkForCliUpdate({
415
+ args: process.argv,
416
+ currentVersion: version,
417
+ packageName: name
418
+ });
414
419
  await createCliProgram().parseAsync(process.argv);
415
420
 
416
421
  //#endregion
@@ -1,4 +1,4 @@
1
- import { c as version, i as listComponents, n as lintProject, o as resolveVersion, r as findComponent, s as loadMetadataFile } from "./scanner-CsY3-bVr.mjs";
1
+ import { a as findComponent, d as resolveVersion, f as loadMetadataFile, i as lintProject, m as version, n as getCliUpdateStatus, o as listComponents, p as name } from "./update-check-cp6uTpce.mjs";
2
2
  import process from "node:process";
3
3
  import { McpServer, StdioServerTransport } from "@modelcontextprotocol/server";
4
4
  import * as z from "zod/v4";
@@ -6,6 +6,7 @@ import * as z from "zod/v4";
6
6
  //#region src/mcp/prompts.ts
7
7
  const WOT_EXPERT_PROMPT = [
8
8
  "You are a wot-ui expert assistant.",
9
+ "Use wot_status when the user asks about tool health, updates, or unexpected missing metadata.",
9
10
  "Always query component metadata before generating code.",
10
11
  "Prefer using wot_list, wot_info, wot_doc, and wot_token before writing UI code.",
11
12
  "Assume only wot-ui v2 is supported by this server."
@@ -21,7 +22,33 @@ const WOT_PAGE_GENERATOR_PROMPT = [
21
22
  function jsonText(value) {
22
23
  return JSON.stringify(value, null, 2);
23
24
  }
24
- function registerMcpTools(server) {
25
+ function registerMcpTools(server, options = {}) {
26
+ server.registerTool("wot_status", {
27
+ description: "Get wot-ui MCP server and CLI update status.",
28
+ inputSchema: z.object({}),
29
+ annotations: {
30
+ readOnlyHint: true,
31
+ destructiveHint: false,
32
+ idempotentHint: true,
33
+ openWorldHint: true
34
+ }
35
+ }, async () => {
36
+ const update = await getCliUpdateStatus({
37
+ currentVersion: version,
38
+ packageName: name,
39
+ ...options.updateCheckOptions
40
+ });
41
+ return { content: [{
42
+ type: "text",
43
+ text: jsonText({
44
+ server: {
45
+ name: "wot-ui",
46
+ version
47
+ },
48
+ cli: update
49
+ })
50
+ }] };
51
+ });
25
52
  server.registerTool("wot_list", {
26
53
  description: "List available wot-ui components.",
27
54
  inputSchema: z.object({ version: z.string().optional() }),
@@ -217,6 +244,10 @@ async function startMcpServer() {
217
244
  capabilities: { logging: {} }
218
245
  });
219
246
  registerMcpTools(server);
247
+ getCliUpdateStatus({
248
+ currentVersion: version,
249
+ packageName: name
250
+ }).catch(() => {});
220
251
  server.registerPrompt("wot-expert", { description: "General wot-ui expert workflow." }, async () => ({ messages: [{
221
252
  role: "assistant",
222
253
  content: {
@@ -0,0 +1,547 @@
1
+ import process from "node:process";
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
3
+ import { dirname, join, relative, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { gunzipSync } from "node:zlib";
6
+ import { parse } from "@vue/compiler-sfc";
7
+ import { homedir } from "node:os";
8
+
9
+ //#region package.json
10
+ var name = "@wot-ui/cli";
11
+ var version = "1.0.3";
12
+
13
+ //#endregion
14
+ //#region src/data/loader.ts
15
+ const currentDir = dirname(fileURLToPath(import.meta.url));
16
+ function resolveDataDir() {
17
+ const candidates = [
18
+ join(currentDir, "..", "data"),
19
+ join(currentDir, "..", "..", "data"),
20
+ join(currentDir, "data")
21
+ ];
22
+ for (const candidate of candidates) if (existsSync(join(candidate, "versions.json")) || existsSync(join(candidate, "versions.json.gz"))) return candidate;
23
+ throw new Error("Unable to locate bundled data directory");
24
+ }
25
+ const dataDir = resolveDataDir();
26
+ function readJsonFile(baseName) {
27
+ const jsonPath = join(dataDir, `${baseName}.json`);
28
+ if (existsSync(jsonPath)) return JSON.parse(readFileSync(jsonPath, "utf8"));
29
+ const gzipPath = join(dataDir, `${baseName}.json.gz`);
30
+ if (existsSync(gzipPath)) {
31
+ const compressed = readFileSync(gzipPath);
32
+ return JSON.parse(gunzipSync(compressed).toString("utf8"));
33
+ }
34
+ throw new Error(`Data file not found for ${baseName}`);
35
+ }
36
+ function loadVersionsFile() {
37
+ return readJsonFile("versions");
38
+ }
39
+ function loadMetadataFile(versionKey) {
40
+ return readJsonFile(versionKey);
41
+ }
42
+
43
+ //#endregion
44
+ //#region src/data/version.ts
45
+ /** Strip semver range operators (^, ~, >=, >, <=, <, =, whitespace). */
46
+ function stripRange(ver) {
47
+ return ver.replace(/[\^~>=<\s]/g, "");
48
+ }
49
+ /**
50
+ * Returns all stable version strings for major key 'v2',
51
+ * sorted ascending by semver.
52
+ */
53
+ function stableV2Versions() {
54
+ const map = loadVersionsFile().v2 ?? {};
55
+ return Object.values(map).filter((v) => !v.includes("-")).sort((a, b) => {
56
+ const pa = a.split(".").map(Number);
57
+ const pb = b.split(".").map(Number);
58
+ for (let i = 0; i < 3; i++) {
59
+ const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
60
+ if (diff !== 0) return diff;
61
+ }
62
+ return 0;
63
+ });
64
+ }
65
+ /**
66
+ * Auto-detect the wot-ui version to use.
67
+ *
68
+ * Priority:
69
+ * 1. --version flag (flagVersion arg)
70
+ * 2. node_modules/@wot-ui/ui/package.json in cwd
71
+ * 3. package.json dependencies[@wot-ui/ui] in cwd
72
+ * 4. Fallback to latest stable version from versions.json
73
+ */
74
+ function detectVersion(flagVersion, cwd) {
75
+ const dir = cwd ?? process.cwd();
76
+ if (flagVersion) return {
77
+ version: flagVersion,
78
+ source: "flag"
79
+ };
80
+ const nmPath = join(dir, "node_modules", "@wot-ui", "ui", "package.json");
81
+ if (existsSync(nmPath)) try {
82
+ const pkg = JSON.parse(readFileSync(nmPath, "utf8"));
83
+ if (pkg.version) return {
84
+ version: pkg.version,
85
+ source: "node_modules"
86
+ };
87
+ } catch {}
88
+ const pkgPath = join(dir, "package.json");
89
+ if (existsSync(pkgPath)) try {
90
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
91
+ const depVersion = pkg.dependencies?.["@wot-ui/ui"] ?? pkg.devDependencies?.["@wot-ui/ui"] ?? pkg.peerDependencies?.["@wot-ui/ui"];
92
+ if (depVersion) return {
93
+ version: stripRange(depVersion),
94
+ source: "package.json"
95
+ };
96
+ } catch {}
97
+ return {
98
+ version: stableV2Versions().at(-1) ?? "2.0.0",
99
+ source: "fallback"
100
+ };
101
+ }
102
+ /**
103
+ * Resolve a version string (from detectVersion or CLI flag) to a data file key.
104
+ *
105
+ * Examples:
106
+ * undefined / 'v2' → 'v2' (major alias, data/v2.json)
107
+ * 'latest' → 'v2.0.4' (latest stable snapshot)
108
+ * '2.0' → 'v2.0.4' (minor → lookup in versions.json)
109
+ * '2.0.4' → 'v2.0.4' (exact patch)
110
+ * '2.0.0-alpha.5' → 'v2.0.0-alpha.5' (pre-release exact)
111
+ */
112
+ function resolveVersion(requested) {
113
+ if (!requested || requested === "v2") return "v2";
114
+ const normalized = requested.trim();
115
+ if (normalized === "latest") {
116
+ const latest = stableV2Versions().at(-1);
117
+ if (!latest) return "v2";
118
+ return `v${latest}`;
119
+ }
120
+ const map = loadVersionsFile().v2 ?? {};
121
+ if (/^\d+\.\d+$/.test(normalized)) {
122
+ const patch = map[normalized];
123
+ if (!patch) throw new Error(`Unsupported wot-ui version: ${requested}`);
124
+ return `v${patch}`;
125
+ }
126
+ if (/^\d+\.\d+\.\d+/.test(normalized)) {
127
+ if (normalized.split(".")[0] !== "2") throw new Error(`Unsupported wot-ui version: ${requested}`);
128
+ return `v${normalized}`;
129
+ }
130
+ throw new Error(`Unsupported wot-ui version: ${requested}`);
131
+ }
132
+
133
+ //#endregion
134
+ //#region src/utils/terminal.ts
135
+ const ANSI = {
136
+ cyan: ["\x1B[36m", "\x1B[39m"],
137
+ dim: ["\x1B[2m", "\x1B[22m"],
138
+ green: ["\x1B[32m", "\x1B[39m"],
139
+ red: ["\x1B[31m", "\x1B[39m"],
140
+ yellow: ["\x1B[33m", "\x1B[39m"]
141
+ };
142
+ function supportsColor(options = {}) {
143
+ const env = options.env ?? process.env;
144
+ if (!(options.isTty ?? process.stderr.isTTY)) return false;
145
+ if ("NO_COLOR" in env || env.FORCE_COLOR === "0" || env.TERM === "dumb") return false;
146
+ return true;
147
+ }
148
+ function writeStderrLine(message) {
149
+ process.stderr.write(`${message}\n`);
150
+ }
151
+ function formatLogMessage(level, message, options = {}) {
152
+ const color = createColorizer(options);
153
+ return `${color.dim("[wot]")} ${styleLevel(level, message, color)}`;
154
+ }
155
+ function formatStatusLabel(status, options = {}) {
156
+ const normalized = status.toUpperCase();
157
+ const color = createColorizer(options);
158
+ if (status === "ok" || status === "pass") return color.green(normalized);
159
+ if (status === "warn" || status === "warning") return color.yellow(normalized);
160
+ return color.red(normalized);
161
+ }
162
+ function formatCommand(command, options = {}) {
163
+ return createColorizer(options).cyan(command);
164
+ }
165
+ function formatUpdateNotice(status, options = {}) {
166
+ const color = createColorizer(options);
167
+ const currentVersion = color.dim(status.currentVersion);
168
+ const latestVersion = color.green(status.latestVersion ?? "unknown");
169
+ return [
170
+ formatLogMessage("update", "Update available", options),
171
+ `${color.dim("[wot]")} ${status.packageName} ${currentVersion} -> ${latestVersion}`,
172
+ `${color.dim("[wot]")} Run: ${formatCommand(status.command, options)}`
173
+ ].join("\n");
174
+ }
175
+ function createColorizer(options) {
176
+ const enabled = supportsColor(options);
177
+ return {
178
+ cyan: (value) => applyAnsi(value, ANSI.cyan, enabled),
179
+ dim: (value) => applyAnsi(value, ANSI.dim, enabled),
180
+ green: (value) => applyAnsi(value, ANSI.green, enabled),
181
+ red: (value) => applyAnsi(value, ANSI.red, enabled),
182
+ yellow: (value) => applyAnsi(value, ANSI.yellow, enabled)
183
+ };
184
+ }
185
+ function styleLevel(level, message, color) {
186
+ if (level === "error") return color.red(message);
187
+ if (level === "success") return color.green(message);
188
+ if (level === "warn" || level === "update") return color.yellow(message);
189
+ if (level === "hint") return color.cyan(message);
190
+ return message;
191
+ }
192
+ function applyAnsi(value, code, enabled) {
193
+ return enabled ? `${code[0]}${value}${code[1]}` : value;
194
+ }
195
+
196
+ //#endregion
197
+ //#region src/data/metadata.ts
198
+ function loadResolvedMetadata(version$1) {
199
+ return loadMetadataFile(resolveVersion(version$1));
200
+ }
201
+ function listComponents(version$1) {
202
+ return loadResolvedMetadata(version$1).components;
203
+ }
204
+ function findComponent(name$1, version$1) {
205
+ const normalized = name$1.trim().toLowerCase();
206
+ return listComponents(version$1).find((component) => component.name.toLowerCase() === normalized || component.tag.toLowerCase() === normalized);
207
+ }
208
+
209
+ //#endregion
210
+ //#region src/utils/files.ts
211
+ const DEFAULT_IGNORES = new Set([
212
+ ".git",
213
+ ".idea",
214
+ ".output",
215
+ ".turbo",
216
+ ".vscode",
217
+ "dist",
218
+ "build",
219
+ "coverage",
220
+ "node_modules"
221
+ ]);
222
+ function walkFiles(rootDir, extensions) {
223
+ const results = [];
224
+ function visit(dir) {
225
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
226
+ if (DEFAULT_IGNORES.has(entry.name)) continue;
227
+ const fullPath = join(dir, entry.name);
228
+ if (entry.isDirectory()) {
229
+ visit(fullPath);
230
+ continue;
231
+ }
232
+ if (extensions.some((extension) => entry.name.endsWith(extension))) results.push(fullPath);
233
+ }
234
+ }
235
+ visit(rootDir);
236
+ return results;
237
+ }
238
+ function safeRelative(rootDir, filePath) {
239
+ return relative(rootDir, filePath) || ".";
240
+ }
241
+
242
+ //#endregion
243
+ //#region src/utils/scanner.ts
244
+ const IMPORT_RE = /from\s+['"]([^'"]*wot[^'"]*)['"]/g;
245
+ const TAG_RE = /<\s*(wd-[a-z0-9-]+)/gi;
246
+ const BUTTON_RE = /<wd-button\b([^>]*)>([\s\S]*?)<\/wd-button>|<wd-button\b([^>]*)\/>/gi;
247
+ function getLineNumber(source, index) {
248
+ return source.slice(0, index).split("\n").length;
249
+ }
250
+ function collectTemplateTags(content) {
251
+ const counts = /* @__PURE__ */ new Map();
252
+ for (const match of content.matchAll(TAG_RE)) {
253
+ const tag = match[1]?.toLowerCase();
254
+ if (!tag) continue;
255
+ counts.set(tag, (counts.get(tag) ?? 0) + 1);
256
+ }
257
+ return counts;
258
+ }
259
+ function collectImports(scriptContent) {
260
+ const imports = /* @__PURE__ */ new Set();
261
+ for (const match of scriptContent.matchAll(IMPORT_RE)) if (match[1]) imports.add(match[1]);
262
+ return [...imports];
263
+ }
264
+ function analyzeUsage(targetDir, version$1) {
265
+ const dir = resolve(targetDir);
266
+ const files = walkFiles(dir, [".vue"]);
267
+ const knownByTag = new Map(listComponents(version$1).map((component) => [component.tag.toLowerCase(), component]));
268
+ const usageMap = /* @__PURE__ */ new Map();
269
+ const imports = /* @__PURE__ */ new Set();
270
+ for (const file of files) {
271
+ const parsed = parse(readFileSync(file, "utf8"), { filename: file });
272
+ const template = parsed.descriptor.template?.content ?? "";
273
+ const script = [parsed.descriptor.script?.content ?? "", parsed.descriptor.scriptSetup?.content ?? ""].filter(Boolean).join("\n");
274
+ for (const item of collectImports(script)) imports.add(item);
275
+ for (const [tag, count] of collectTemplateTags(template)) {
276
+ const known = knownByTag.get(tag);
277
+ const key = known?.name ?? tag;
278
+ const existing = usageMap.get(key);
279
+ if (existing) {
280
+ existing.count += count;
281
+ if (!existing.files.includes(safeRelative(dir, file))) existing.files.push(safeRelative(dir, file));
282
+ continue;
283
+ }
284
+ usageMap.set(key, {
285
+ name: known?.name ?? tag,
286
+ tag,
287
+ count,
288
+ files: [safeRelative(dir, file)]
289
+ });
290
+ }
291
+ }
292
+ return {
293
+ scannedFiles: files.length,
294
+ components: [...usageMap.values()].sort((left, right) => right.count - left.count || left.name.localeCompare(right.name)),
295
+ imports: [...imports].sort()
296
+ };
297
+ }
298
+ function lintProject(targetDir, version$1) {
299
+ const dir = resolve(targetDir);
300
+ const files = walkFiles(dir, [".vue"]);
301
+ const issues = [];
302
+ for (const file of files) {
303
+ const template = parse(readFileSync(file, "utf8"), { filename: file }).descriptor.template?.content ?? "";
304
+ for (const match of template.matchAll(TAG_RE)) {
305
+ const tag = match[1]?.toLowerCase();
306
+ if (!tag) continue;
307
+ if (!findComponent(tag, version$1)) issues.push({
308
+ file: safeRelative(dir, file),
309
+ line: getLineNumber(template, match.index ?? 0),
310
+ rule: "unknown-component",
311
+ severity: "warning",
312
+ message: `Unknown wot-ui component tag: ${tag}`
313
+ });
314
+ }
315
+ for (const match of template.matchAll(BUTTON_RE)) {
316
+ const attrs = (match[1] ?? match[3] ?? "").trim();
317
+ const body = (match[2] ?? "").replace(/<[^>]+>/g, "").trim();
318
+ if (!/\bicon\s*=/.test(attrs) && !body) issues.push({
319
+ file: safeRelative(dir, file),
320
+ line: getLineNumber(template, match.index ?? 0),
321
+ rule: "button-content",
322
+ severity: "warning",
323
+ message: "wd-button should include visible text content or an icon attribute."
324
+ });
325
+ const component = findComponent("wd-button", version$1);
326
+ for (const prop of component?.props ?? []) {
327
+ if (!prop.deprecated) continue;
328
+ if (!(/* @__PURE__ */ new RegExp(`\\b${prop.name}\\b`)).test(attrs)) continue;
329
+ issues.push({
330
+ file: safeRelative(dir, file),
331
+ line: getLineNumber(template, match.index ?? 0),
332
+ rule: "deprecated-prop",
333
+ severity: "warning",
334
+ message: prop.replacement ? `Deprecated prop ${prop.name} detected on wd-button. Use ${prop.replacement} instead.` : `Deprecated prop ${prop.name} detected on wd-button.`
335
+ });
336
+ }
337
+ }
338
+ }
339
+ return {
340
+ scannedFiles: files.length,
341
+ issues
342
+ };
343
+ }
344
+
345
+ //#endregion
346
+ //#region src/utils/update-check.ts
347
+ const DEFAULT_CHECK_INTERVAL_MS = 1440 * 60 * 1e3;
348
+ const DEFAULT_TIMEOUT_MS = 1500;
349
+ const DEFAULT_REGISTRY = "https://registry.npmjs.org";
350
+ function compareSemver(a, b) {
351
+ const parsedA = parseSemver(a);
352
+ const parsedB = parseSemver(b);
353
+ if (!parsedA || !parsedB) return 0;
354
+ for (const index of [
355
+ 0,
356
+ 1,
357
+ 2
358
+ ]) {
359
+ const diff = parsedA[index] - parsedB[index];
360
+ if (diff !== 0) return diff > 0 ? 1 : -1;
361
+ }
362
+ return comparePrerelease(parsedA[3], parsedB[3]);
363
+ }
364
+ function shouldCheckForCliUpdate(args = process.argv, env = process.env, isTty = process.stderr.isTTY) {
365
+ if (!isTty) return false;
366
+ if (isUpdateCheckDisabled(env) || isTruthyEnv(env.CI) || env.NODE_ENV === "test") return false;
367
+ const userArgs = args.slice(2);
368
+ if (userArgs.some((arg) => arg === "-V" || arg === "-h" || arg === "--help")) return false;
369
+ const command = userArgs.find((arg) => !arg.startsWith("-"));
370
+ return command !== "mcp" && command !== "help";
371
+ }
372
+ function checkForCliUpdate(options) {
373
+ const env = options.env ?? process.env;
374
+ const args = options.args ?? process.argv;
375
+ const stderr = options.stderr ?? process.stderr;
376
+ const isTty = options.isTty ?? process.stderr.isTTY;
377
+ if (!shouldCheckForCliUpdate(args, env, isTty)) return;
378
+ try {
379
+ const status = getCachedCliUpdateStatus(options);
380
+ if (status.updateAvailable && status.latestVersion) stderr.write(`${formatUpdateNotice(status, {
381
+ env,
382
+ isTty
383
+ })}\n`);
384
+ } catch {}
385
+ }
386
+ function getCachedCliUpdateStatus(options) {
387
+ const env = options.env ?? process.env;
388
+ const baseStatus = createBaseStatus(options, env);
389
+ if (baseStatus.disabled) return {
390
+ ...baseStatus,
391
+ cached: false,
392
+ updateAvailable: false
393
+ };
394
+ const now = options.now ?? Date.now();
395
+ const cached = readCache(options.cacheFile ?? getDefaultCacheFile(env));
396
+ const intervalMs = options.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS;
397
+ const cacheIsFresh = !!cached && now - cached.checkedAt < intervalMs;
398
+ const latestVersion = cacheIsFresh ? cached.latestVersion : void 0;
399
+ return {
400
+ ...baseStatus,
401
+ cached: cacheIsFresh,
402
+ checkedAt: cacheIsFresh ? cached.checkedAt : void 0,
403
+ latestVersion,
404
+ updateAvailable: !!latestVersion && compareSemver(latestVersion, options.currentVersion) > 0
405
+ };
406
+ }
407
+ async function getCliUpdateStatus(options) {
408
+ const env = options.env ?? process.env;
409
+ const baseStatus = createBaseStatus(options, env);
410
+ if (baseStatus.disabled) return {
411
+ ...baseStatus,
412
+ cached: false,
413
+ updateAvailable: false
414
+ };
415
+ const now = options.now ?? Date.now();
416
+ const cacheFile = options.cacheFile ?? getDefaultCacheFile(env);
417
+ const cached = readCache(cacheFile);
418
+ const intervalMs = options.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS;
419
+ const cacheIsFresh = !!cached && now - cached.checkedAt < intervalMs;
420
+ const result = cacheIsFresh ? cached : await fetchAndCacheLatestVersion(options, cacheFile, now);
421
+ const latestVersion = result.latestVersion;
422
+ return {
423
+ ...baseStatus,
424
+ cached: cacheIsFresh,
425
+ checkedAt: result.checkedAt,
426
+ latestVersion,
427
+ updateAvailable: !!latestVersion && compareSemver(latestVersion, options.currentVersion) > 0
428
+ };
429
+ }
430
+ function createBaseStatus(options, env) {
431
+ return {
432
+ command: `npm install -g ${options.packageName}`,
433
+ currentVersion: options.currentVersion,
434
+ disabled: isUpdateCheckDisabled(env),
435
+ packageName: options.packageName
436
+ };
437
+ }
438
+ function parseSemver(version$1) {
439
+ const match = version$1.trim().replace(/^v/, "").match(/^(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?(?:\+.*)?$/);
440
+ if (!match) return void 0;
441
+ return [
442
+ Number(match[1]),
443
+ Number(match[2]),
444
+ Number(match[3]),
445
+ match[4]
446
+ ];
447
+ }
448
+ function comparePrerelease(a, b) {
449
+ if (!a && !b) return 0;
450
+ if (!a) return 1;
451
+ if (!b) return -1;
452
+ const identifiersA = a.split(".");
453
+ const identifiersB = b.split(".");
454
+ const length = Math.max(identifiersA.length, identifiersB.length);
455
+ for (let index = 0; index < length; index++) {
456
+ const identifierA = identifiersA[index];
457
+ const identifierB = identifiersB[index];
458
+ if (identifierA === void 0) return -1;
459
+ if (identifierB === void 0) return 1;
460
+ if (identifierA === identifierB) continue;
461
+ const numberA = parseNumericIdentifier(identifierA);
462
+ const numberB = parseNumericIdentifier(identifierB);
463
+ if (numberA !== void 0 && numberB !== void 0) return numberA > numberB ? 1 : -1;
464
+ if (numberA !== void 0) return -1;
465
+ if (numberB !== void 0) return 1;
466
+ return identifierA > identifierB ? 1 : -1;
467
+ }
468
+ return 0;
469
+ }
470
+ function parseNumericIdentifier(identifier) {
471
+ if (!/^(?:0|[1-9]\d*)$/.test(identifier)) return void 0;
472
+ return Number(identifier);
473
+ }
474
+ function isTruthyEnv(value) {
475
+ return !!value && value !== "0" && value !== "false";
476
+ }
477
+ function isUpdateCheckDisabled(env) {
478
+ return isTruthyEnv(env.WOT_DISABLE_UPDATE_CHECK) || isTruthyEnv(env.NO_UPDATE_NOTIFIER);
479
+ }
480
+ function getDefaultCacheFile(env) {
481
+ return join(env.XDG_CACHE_HOME ? join(env.XDG_CACHE_HOME, "open-wot") : join(homedir(), ".cache", "open-wot"), "update-check.json");
482
+ }
483
+ function readCache(cacheFile) {
484
+ if (!existsSync(cacheFile)) return void 0;
485
+ let cache;
486
+ try {
487
+ cache = JSON.parse(readFileSync(cacheFile, "utf8"));
488
+ } catch {
489
+ return;
490
+ }
491
+ if (!cache || typeof cache !== "object" || typeof cache.checkedAt !== "number") return void 0;
492
+ return {
493
+ checkedAt: cache.checkedAt,
494
+ latestVersion: typeof cache.latestVersion === "string" ? cache.latestVersion : void 0
495
+ };
496
+ }
497
+ async function fetchAndCacheLatestVersion(options, cacheFile, now) {
498
+ let latestVersion;
499
+ try {
500
+ latestVersion = await fetchLatestVersion(options.packageName, options.registry ?? options.env?.npm_config_registry ?? DEFAULT_REGISTRY, options.fetchFn, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
501
+ } catch {
502
+ latestVersion = void 0;
503
+ }
504
+ const cache = {
505
+ checkedAt: now,
506
+ latestVersion
507
+ };
508
+ writeCache(cacheFile, cache);
509
+ return cache;
510
+ }
511
+ async function fetchLatestVersion(packageName, registry, fetchFn, timeoutMs) {
512
+ const request = fetchFn ?? globalThis.fetch;
513
+ if (typeof request !== "function") return void 0;
514
+ const controller = new AbortController();
515
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
516
+ try {
517
+ const response = await request(`${registry.replace(/\/+$/, "")}/${encodePackageName(packageName)}/latest`, {
518
+ headers: {
519
+ "accept": "application/json",
520
+ "user-agent": `${packageName} update-check`
521
+ },
522
+ signal: controller.signal
523
+ });
524
+ if (!response.ok) return void 0;
525
+ const json = await response.json();
526
+ if (isRegistryLatestResponse(json)) return json.version;
527
+ } finally {
528
+ clearTimeout(timeout);
529
+ }
530
+ }
531
+ function encodePackageName(packageName) {
532
+ if (!packageName.startsWith("@")) return encodeURIComponent(packageName);
533
+ const [scope, name$1] = packageName.split("/");
534
+ return `${scope}%2f${name$1}`;
535
+ }
536
+ function isRegistryLatestResponse(value) {
537
+ return typeof value === "object" && value !== null && "version" in value && typeof value.version === "string";
538
+ }
539
+ function writeCache(cacheFile, cache) {
540
+ try {
541
+ mkdirSync(dirname(cacheFile), { recursive: true });
542
+ writeFileSync(cacheFile, `${JSON.stringify(cache, null, 2)}\n`);
543
+ } catch {}
544
+ }
545
+
546
+ //#endregion
547
+ export { findComponent as a, formatStatusLabel as c, resolveVersion as d, loadMetadataFile as f, lintProject as i, writeStderrLine as l, version as m, getCliUpdateStatus as n, listComponents as o, name as p, analyzeUsage as r, formatLogMessage as s, checkForCliUpdate as t, detectVersion as u };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wot-ui/cli",
3
3
  "type": "module",
4
- "version": "1.0.1",
4
+ "version": "1.0.3",
5
5
  "description": "面向 wot-ui 的 CLI、MCP 与数据提取工具集",
6
6
  "license": "MIT",
7
7
  "funding": "https://github.com/sponsors/antfu",
@@ -1,281 +0,0 @@
1
- import process from "node:process";
2
- import { existsSync, readFileSync, readdirSync } from "node:fs";
3
- import { dirname, join, relative, resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { gunzipSync } from "node:zlib";
6
- import { parse } from "@vue/compiler-sfc";
7
-
8
- //#region package.json
9
- var version = "1.0.1";
10
-
11
- //#endregion
12
- //#region src/data/loader.ts
13
- const currentDir = dirname(fileURLToPath(import.meta.url));
14
- function resolveDataDir() {
15
- const candidates = [
16
- join(currentDir, "..", "data"),
17
- join(currentDir, "..", "..", "data"),
18
- join(currentDir, "data")
19
- ];
20
- for (const candidate of candidates) if (existsSync(join(candidate, "versions.json")) || existsSync(join(candidate, "versions.json.gz"))) return candidate;
21
- throw new Error("Unable to locate bundled data directory");
22
- }
23
- const dataDir = resolveDataDir();
24
- function readJsonFile(baseName) {
25
- const jsonPath = join(dataDir, `${baseName}.json`);
26
- if (existsSync(jsonPath)) return JSON.parse(readFileSync(jsonPath, "utf8"));
27
- const gzipPath = join(dataDir, `${baseName}.json.gz`);
28
- if (existsSync(gzipPath)) {
29
- const compressed = readFileSync(gzipPath);
30
- return JSON.parse(gunzipSync(compressed).toString("utf8"));
31
- }
32
- throw new Error(`Data file not found for ${baseName}`);
33
- }
34
- function loadVersionsFile() {
35
- return readJsonFile("versions");
36
- }
37
- function loadMetadataFile(versionKey) {
38
- return readJsonFile(versionKey);
39
- }
40
-
41
- //#endregion
42
- //#region src/data/version.ts
43
- /** Strip semver range operators (^, ~, >=, >, <=, <, =, whitespace). */
44
- function stripRange(ver) {
45
- return ver.replace(/[\^~>=<\s]/g, "");
46
- }
47
- /**
48
- * Returns all stable version strings for major key 'v2',
49
- * sorted ascending by semver.
50
- */
51
- function stableV2Versions() {
52
- const map = loadVersionsFile().v2 ?? {};
53
- return Object.values(map).filter((v) => !v.includes("-")).sort((a, b) => {
54
- const pa = a.split(".").map(Number);
55
- const pb = b.split(".").map(Number);
56
- for (let i = 0; i < 3; i++) {
57
- const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
58
- if (diff !== 0) return diff;
59
- }
60
- return 0;
61
- });
62
- }
63
- /**
64
- * Auto-detect the wot-ui version to use.
65
- *
66
- * Priority:
67
- * 1. --version flag (flagVersion arg)
68
- * 2. node_modules/@wot-ui/ui/package.json in cwd
69
- * 3. package.json dependencies[@wot-ui/ui] in cwd
70
- * 4. Fallback to latest stable version from versions.json
71
- */
72
- function detectVersion(flagVersion, cwd) {
73
- const dir = cwd ?? process.cwd();
74
- if (flagVersion) return {
75
- version: flagVersion,
76
- source: "flag"
77
- };
78
- const nmPath = join(dir, "node_modules", "@wot-ui", "ui", "package.json");
79
- if (existsSync(nmPath)) try {
80
- const pkg = JSON.parse(readFileSync(nmPath, "utf8"));
81
- if (pkg.version) return {
82
- version: pkg.version,
83
- source: "node_modules"
84
- };
85
- } catch {}
86
- const pkgPath = join(dir, "package.json");
87
- if (existsSync(pkgPath)) try {
88
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
89
- const depVersion = pkg.dependencies?.["@wot-ui/ui"] ?? pkg.devDependencies?.["@wot-ui/ui"] ?? pkg.peerDependencies?.["@wot-ui/ui"];
90
- if (depVersion) return {
91
- version: stripRange(depVersion),
92
- source: "package.json"
93
- };
94
- } catch {}
95
- return {
96
- version: stableV2Versions().at(-1) ?? "2.0.0",
97
- source: "fallback"
98
- };
99
- }
100
- /**
101
- * Resolve a version string (from detectVersion or CLI flag) to a data file key.
102
- *
103
- * Examples:
104
- * undefined / 'v2' → 'v2' (major alias, data/v2.json)
105
- * 'latest' → 'v2.0.4' (latest stable snapshot)
106
- * '2.0' → 'v2.0.4' (minor → lookup in versions.json)
107
- * '2.0.4' → 'v2.0.4' (exact patch)
108
- * '2.0.0-alpha.5' → 'v2.0.0-alpha.5' (pre-release exact)
109
- */
110
- function resolveVersion(requested) {
111
- if (!requested || requested === "v2") return "v2";
112
- const normalized = requested.trim();
113
- if (normalized === "latest") {
114
- const latest = stableV2Versions().at(-1);
115
- if (!latest) return "v2";
116
- return `v${latest}`;
117
- }
118
- const map = loadVersionsFile().v2 ?? {};
119
- if (/^\d+\.\d+$/.test(normalized)) {
120
- const patch = map[normalized];
121
- if (!patch) throw new Error(`Unsupported wot-ui version: ${requested}`);
122
- return `v${patch}`;
123
- }
124
- if (/^\d+\.\d+\.\d+/.test(normalized)) {
125
- if (normalized.split(".")[0] !== "2") throw new Error(`Unsupported wot-ui version: ${requested}`);
126
- return `v${normalized}`;
127
- }
128
- throw new Error(`Unsupported wot-ui version: ${requested}`);
129
- }
130
-
131
- //#endregion
132
- //#region src/data/metadata.ts
133
- function loadResolvedMetadata(version$1) {
134
- return loadMetadataFile(resolveVersion(version$1));
135
- }
136
- function listComponents(version$1) {
137
- return loadResolvedMetadata(version$1).components;
138
- }
139
- function findComponent(name, version$1) {
140
- const normalized = name.trim().toLowerCase();
141
- return listComponents(version$1).find((component) => component.name.toLowerCase() === normalized || component.tag.toLowerCase() === normalized);
142
- }
143
-
144
- //#endregion
145
- //#region src/utils/files.ts
146
- const DEFAULT_IGNORES = new Set([
147
- ".git",
148
- ".idea",
149
- ".output",
150
- ".turbo",
151
- ".vscode",
152
- "dist",
153
- "build",
154
- "coverage",
155
- "node_modules"
156
- ]);
157
- function walkFiles(rootDir, extensions) {
158
- const results = [];
159
- function visit(dir) {
160
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
161
- if (DEFAULT_IGNORES.has(entry.name)) continue;
162
- const fullPath = join(dir, entry.name);
163
- if (entry.isDirectory()) {
164
- visit(fullPath);
165
- continue;
166
- }
167
- if (extensions.some((extension) => entry.name.endsWith(extension))) results.push(fullPath);
168
- }
169
- }
170
- visit(rootDir);
171
- return results;
172
- }
173
- function safeRelative(rootDir, filePath) {
174
- return relative(rootDir, filePath) || ".";
175
- }
176
-
177
- //#endregion
178
- //#region src/utils/scanner.ts
179
- const IMPORT_RE = /from\s+['"]([^'"]*wot[^'"]*)['"]/g;
180
- const TAG_RE = /<\s*(wd-[a-z0-9-]+)/gi;
181
- const BUTTON_RE = /<wd-button\b([^>]*)>([\s\S]*?)<\/wd-button>|<wd-button\b([^>]*)\/>/gi;
182
- function getLineNumber(source, index) {
183
- return source.slice(0, index).split("\n").length;
184
- }
185
- function collectTemplateTags(content) {
186
- const counts = /* @__PURE__ */ new Map();
187
- for (const match of content.matchAll(TAG_RE)) {
188
- const tag = match[1]?.toLowerCase();
189
- if (!tag) continue;
190
- counts.set(tag, (counts.get(tag) ?? 0) + 1);
191
- }
192
- return counts;
193
- }
194
- function collectImports(scriptContent) {
195
- const imports = /* @__PURE__ */ new Set();
196
- for (const match of scriptContent.matchAll(IMPORT_RE)) if (match[1]) imports.add(match[1]);
197
- return [...imports];
198
- }
199
- function analyzeUsage(targetDir, version$1) {
200
- const dir = resolve(targetDir);
201
- const files = walkFiles(dir, [".vue"]);
202
- const knownByTag = new Map(listComponents(version$1).map((component) => [component.tag.toLowerCase(), component]));
203
- const usageMap = /* @__PURE__ */ new Map();
204
- const imports = /* @__PURE__ */ new Set();
205
- for (const file of files) {
206
- const parsed = parse(readFileSync(file, "utf8"), { filename: file });
207
- const template = parsed.descriptor.template?.content ?? "";
208
- const script = [parsed.descriptor.script?.content ?? "", parsed.descriptor.scriptSetup?.content ?? ""].filter(Boolean).join("\n");
209
- for (const item of collectImports(script)) imports.add(item);
210
- for (const [tag, count] of collectTemplateTags(template)) {
211
- const known = knownByTag.get(tag);
212
- const key = known?.name ?? tag;
213
- const existing = usageMap.get(key);
214
- if (existing) {
215
- existing.count += count;
216
- if (!existing.files.includes(safeRelative(dir, file))) existing.files.push(safeRelative(dir, file));
217
- continue;
218
- }
219
- usageMap.set(key, {
220
- name: known?.name ?? tag,
221
- tag,
222
- count,
223
- files: [safeRelative(dir, file)]
224
- });
225
- }
226
- }
227
- return {
228
- scannedFiles: files.length,
229
- components: [...usageMap.values()].sort((left, right) => right.count - left.count || left.name.localeCompare(right.name)),
230
- imports: [...imports].sort()
231
- };
232
- }
233
- function lintProject(targetDir, version$1) {
234
- const dir = resolve(targetDir);
235
- const files = walkFiles(dir, [".vue"]);
236
- const issues = [];
237
- for (const file of files) {
238
- const template = parse(readFileSync(file, "utf8"), { filename: file }).descriptor.template?.content ?? "";
239
- for (const match of template.matchAll(TAG_RE)) {
240
- const tag = match[1]?.toLowerCase();
241
- if (!tag) continue;
242
- if (!findComponent(tag, version$1)) issues.push({
243
- file: safeRelative(dir, file),
244
- line: getLineNumber(template, match.index ?? 0),
245
- rule: "unknown-component",
246
- severity: "warning",
247
- message: `Unknown wot-ui component tag: ${tag}`
248
- });
249
- }
250
- for (const match of template.matchAll(BUTTON_RE)) {
251
- const attrs = (match[1] ?? match[3] ?? "").trim();
252
- const body = (match[2] ?? "").replace(/<[^>]+>/g, "").trim();
253
- if (!/\bicon\s*=/.test(attrs) && !body) issues.push({
254
- file: safeRelative(dir, file),
255
- line: getLineNumber(template, match.index ?? 0),
256
- rule: "button-content",
257
- severity: "warning",
258
- message: "wd-button should include visible text content or an icon attribute."
259
- });
260
- const component = findComponent("wd-button", version$1);
261
- for (const prop of component?.props ?? []) {
262
- if (!prop.deprecated) continue;
263
- if (!(/* @__PURE__ */ new RegExp(`\\b${prop.name}\\b`)).test(attrs)) continue;
264
- issues.push({
265
- file: safeRelative(dir, file),
266
- line: getLineNumber(template, match.index ?? 0),
267
- rule: "deprecated-prop",
268
- severity: "warning",
269
- message: prop.replacement ? `Deprecated prop ${prop.name} detected on wd-button. Use ${prop.replacement} instead.` : `Deprecated prop ${prop.name} detected on wd-button.`
270
- });
271
- }
272
- }
273
- }
274
- return {
275
- scannedFiles: files.length,
276
- issues
277
- };
278
- }
279
-
280
- //#endregion
281
- export { detectVersion as a, version as c, listComponents as i, lintProject as n, resolveVersion as o, findComponent as r, loadMetadataFile as s, analyzeUsage as t };