🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@wot-ui/cli

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@wot-ui/cli - npm Package Compare versions

Comparing version
1.0.0
to
1.0.1
data/v2.0.5.json.gz

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

+281
import process from "node:process";
import { existsSync, readFileSync, readdirSync } from "node:fs";
import { dirname, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { gunzipSync } from "node:zlib";
import { parse } from "@vue/compiler-sfc";
//#region package.json
var version = "1.0.1";
//#endregion
//#region src/data/loader.ts
const currentDir = dirname(fileURLToPath(import.meta.url));
function resolveDataDir() {
const candidates = [
join(currentDir, "..", "data"),
join(currentDir, "..", "..", "data"),
join(currentDir, "data")
];
for (const candidate of candidates) if (existsSync(join(candidate, "versions.json")) || existsSync(join(candidate, "versions.json.gz"))) return candidate;
throw new Error("Unable to locate bundled data directory");
}
const dataDir = resolveDataDir();
function readJsonFile(baseName) {
const jsonPath = join(dataDir, `${baseName}.json`);
if (existsSync(jsonPath)) return JSON.parse(readFileSync(jsonPath, "utf8"));
const gzipPath = join(dataDir, `${baseName}.json.gz`);
if (existsSync(gzipPath)) {
const compressed = readFileSync(gzipPath);
return JSON.parse(gunzipSync(compressed).toString("utf8"));
}
throw new Error(`Data file not found for ${baseName}`);
}
function loadVersionsFile() {
return readJsonFile("versions");
}
function loadMetadataFile(versionKey) {
return readJsonFile(versionKey);
}
//#endregion
//#region src/data/version.ts
/** Strip semver range operators (^, ~, >=, >, <=, <, =, whitespace). */
function stripRange(ver) {
return ver.replace(/[\^~>=<\s]/g, "");
}
/**
* Returns all stable version strings for major key 'v2',
* sorted ascending by semver.
*/
function stableV2Versions() {
const map = loadVersionsFile().v2 ?? {};
return Object.values(map).filter((v) => !v.includes("-")).sort((a, b) => {
const pa = a.split(".").map(Number);
const pb = b.split(".").map(Number);
for (let i = 0; i < 3; i++) {
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
if (diff !== 0) return diff;
}
return 0;
});
}
/**
* Auto-detect the wot-ui version to use.
*
* Priority:
* 1. --version flag (flagVersion arg)
* 2. node_modules/@wot-ui/ui/package.json in cwd
* 3. package.json dependencies[@wot-ui/ui] in cwd
* 4. Fallback to latest stable version from versions.json
*/
function detectVersion(flagVersion, cwd) {
const dir = cwd ?? process.cwd();
if (flagVersion) return {
version: flagVersion,
source: "flag"
};
const nmPath = join(dir, "node_modules", "@wot-ui", "ui", "package.json");
if (existsSync(nmPath)) try {
const pkg = JSON.parse(readFileSync(nmPath, "utf8"));
if (pkg.version) return {
version: pkg.version,
source: "node_modules"
};
} catch {}
const pkgPath = join(dir, "package.json");
if (existsSync(pkgPath)) try {
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
const depVersion = pkg.dependencies?.["@wot-ui/ui"] ?? pkg.devDependencies?.["@wot-ui/ui"] ?? pkg.peerDependencies?.["@wot-ui/ui"];
if (depVersion) return {
version: stripRange(depVersion),
source: "package.json"
};
} catch {}
return {
version: stableV2Versions().at(-1) ?? "2.0.0",
source: "fallback"
};
}
/**
* Resolve a version string (from detectVersion or CLI flag) to a data file key.
*
* Examples:
* undefined / 'v2' → 'v2' (major alias, data/v2.json)
* 'latest' → 'v2.0.4' (latest stable snapshot)
* '2.0' → 'v2.0.4' (minor → lookup in versions.json)
* '2.0.4' → 'v2.0.4' (exact patch)
* '2.0.0-alpha.5' → 'v2.0.0-alpha.5' (pre-release exact)
*/
function resolveVersion(requested) {
if (!requested || requested === "v2") return "v2";
const normalized = requested.trim();
if (normalized === "latest") {
const latest = stableV2Versions().at(-1);
if (!latest) return "v2";
return `v${latest}`;
}
const map = loadVersionsFile().v2 ?? {};
if (/^\d+\.\d+$/.test(normalized)) {
const patch = map[normalized];
if (!patch) throw new Error(`Unsupported wot-ui version: ${requested}`);
return `v${patch}`;
}
if (/^\d+\.\d+\.\d+/.test(normalized)) {
if (normalized.split(".")[0] !== "2") throw new Error(`Unsupported wot-ui version: ${requested}`);
return `v${normalized}`;
}
throw new Error(`Unsupported wot-ui version: ${requested}`);
}
//#endregion
//#region src/data/metadata.ts
function loadResolvedMetadata(version$1) {
return loadMetadataFile(resolveVersion(version$1));
}
function listComponents(version$1) {
return loadResolvedMetadata(version$1).components;
}
function findComponent(name, version$1) {
const normalized = name.trim().toLowerCase();
return listComponents(version$1).find((component) => component.name.toLowerCase() === normalized || component.tag.toLowerCase() === normalized);
}
//#endregion
//#region src/utils/files.ts
const DEFAULT_IGNORES = new Set([
".git",
".idea",
".output",
".turbo",
".vscode",
"dist",
"build",
"coverage",
"node_modules"
]);
function walkFiles(rootDir, extensions) {
const results = [];
function visit(dir) {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (DEFAULT_IGNORES.has(entry.name)) continue;
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
visit(fullPath);
continue;
}
if (extensions.some((extension) => entry.name.endsWith(extension))) results.push(fullPath);
}
}
visit(rootDir);
return results;
}
function safeRelative(rootDir, filePath) {
return relative(rootDir, filePath) || ".";
}
//#endregion
//#region src/utils/scanner.ts
const IMPORT_RE = /from\s+['"]([^'"]*wot[^'"]*)['"]/g;
const TAG_RE = /<\s*(wd-[a-z0-9-]+)/gi;
const BUTTON_RE = /<wd-button\b([^>]*)>([\s\S]*?)<\/wd-button>|<wd-button\b([^>]*)\/>/gi;
function getLineNumber(source, index) {
return source.slice(0, index).split("\n").length;
}
function collectTemplateTags(content) {
const counts = /* @__PURE__ */ new Map();
for (const match of content.matchAll(TAG_RE)) {
const tag = match[1]?.toLowerCase();
if (!tag) continue;
counts.set(tag, (counts.get(tag) ?? 0) + 1);
}
return counts;
}
function collectImports(scriptContent) {
const imports = /* @__PURE__ */ new Set();
for (const match of scriptContent.matchAll(IMPORT_RE)) if (match[1]) imports.add(match[1]);
return [...imports];
}
function analyzeUsage(targetDir, version$1) {
const dir = resolve(targetDir);
const files = walkFiles(dir, [".vue"]);
const knownByTag = new Map(listComponents(version$1).map((component) => [component.tag.toLowerCase(), component]));
const usageMap = /* @__PURE__ */ new Map();
const imports = /* @__PURE__ */ new Set();
for (const file of files) {
const parsed = parse(readFileSync(file, "utf8"), { filename: file });
const template = parsed.descriptor.template?.content ?? "";
const script = [parsed.descriptor.script?.content ?? "", parsed.descriptor.scriptSetup?.content ?? ""].filter(Boolean).join("\n");
for (const item of collectImports(script)) imports.add(item);
for (const [tag, count] of collectTemplateTags(template)) {
const known = knownByTag.get(tag);
const key = known?.name ?? tag;
const existing = usageMap.get(key);
if (existing) {
existing.count += count;
if (!existing.files.includes(safeRelative(dir, file))) existing.files.push(safeRelative(dir, file));
continue;
}
usageMap.set(key, {
name: known?.name ?? tag,
tag,
count,
files: [safeRelative(dir, file)]
});
}
}
return {
scannedFiles: files.length,
components: [...usageMap.values()].sort((left, right) => right.count - left.count || left.name.localeCompare(right.name)),
imports: [...imports].sort()
};
}
function lintProject(targetDir, version$1) {
const dir = resolve(targetDir);
const files = walkFiles(dir, [".vue"]);
const issues = [];
for (const file of files) {
const template = parse(readFileSync(file, "utf8"), { filename: file }).descriptor.template?.content ?? "";
for (const match of template.matchAll(TAG_RE)) {
const tag = match[1]?.toLowerCase();
if (!tag) continue;
if (!findComponent(tag, version$1)) issues.push({
file: safeRelative(dir, file),
line: getLineNumber(template, match.index ?? 0),
rule: "unknown-component",
severity: "warning",
message: `Unknown wot-ui component tag: ${tag}`
});
}
for (const match of template.matchAll(BUTTON_RE)) {
const attrs = (match[1] ?? match[3] ?? "").trim();
const body = (match[2] ?? "").replace(/<[^>]+>/g, "").trim();
if (!/\bicon\s*=/.test(attrs) && !body) issues.push({
file: safeRelative(dir, file),
line: getLineNumber(template, match.index ?? 0),
rule: "button-content",
severity: "warning",
message: "wd-button should include visible text content or an icon attribute."
});
const component = findComponent("wd-button", version$1);
for (const prop of component?.props ?? []) {
if (!prop.deprecated) continue;
if (!(/* @__PURE__ */ new RegExp(`\\b${prop.name}\\b`)).test(attrs)) continue;
issues.push({
file: safeRelative(dir, file),
line: getLineNumber(template, match.index ?? 0),
rule: "deprecated-prop",
severity: "warning",
message: prop.replacement ? `Deprecated prop ${prop.name} detected on wd-button. Use ${prop.replacement} instead.` : `Deprecated prop ${prop.name} detected on wd-button.`
});
}
}
}
return {
scannedFiles: files.length,
issues
};
}
//#endregion
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 };
import { c as version, i as listComponents, n as lintProject, o as resolveVersion, r as findComponent, s as loadMetadataFile } from "./scanner-CsY3-bVr.mjs";
import process from "node:process";
import { McpServer, StdioServerTransport } from "@modelcontextprotocol/server";
import * as z from "zod/v4";
//#region src/mcp/prompts.ts
const WOT_EXPERT_PROMPT = [
"You are a wot-ui expert assistant.",
"Always query component metadata before generating code.",
"Prefer using wot_list, wot_info, wot_doc, and wot_token before writing UI code.",
"Assume only wot-ui v2 is supported by this server."
].join(" ");
const WOT_PAGE_GENERATOR_PROMPT = [
"Generate wot-ui pages by first collecting every relevant component API and CSS variable.",
"Prefer existing wd-* components and documented props over ad-hoc custom markup.",
"When theme customization is involved, inspect CSS variables with wot_token first."
].join(" ");
//#endregion
//#region src/mcp/tools.ts
function jsonText(value) {
return JSON.stringify(value, null, 2);
}
function registerMcpTools(server) {
server.registerTool("wot_list", {
description: "List available wot-ui components.",
inputSchema: z.object({ version: z.string().optional() }),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ version: version$1 }) => {
return { content: [{
type: "text",
text: jsonText({ components: listComponents(version$1) })
}] };
});
server.registerTool("wot_info", {
description: "Get props, events, slots, and CSS variables for a component.",
inputSchema: z.object({
component: z.string(),
version: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ component, version: version$1 }) => {
const result = findComponent(component, version$1);
if (!result) return {
isError: true,
content: [{
type: "text",
text: `Component not found: ${component}`
}]
};
return { content: [{
type: "text",
text: jsonText(result)
}] };
});
server.registerTool("wot_doc", {
description: "Get component markdown documentation.",
inputSchema: z.object({
component: z.string(),
version: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ component, version: version$1 }) => {
const result = findComponent(component, version$1);
if (!result?.doc) return {
isError: true,
content: [{
type: "text",
text: `Documentation not found: ${component}`
}]
};
return { content: [{
type: "text",
text: result.doc
}] };
});
server.registerTool("wot_demo", {
description: "Get component demo code or list demos.",
inputSchema: z.object({
component: z.string(),
demo: z.string().optional(),
version: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ component, demo, version: version$1 }) => {
const result = findComponent(component, version$1);
if (!result) return {
isError: true,
content: [{
type: "text",
text: `Component not found: ${component}`
}]
};
if (!demo) return { content: [{
type: "text",
text: jsonText({ demos: result.demos ?? [] })
}] };
const matched = result.demos?.find((item) => item.name.toLowerCase() === demo.toLowerCase());
if (!matched) return {
isError: true,
content: [{
type: "text",
text: `Demo not found: ${demo}`
}]
};
return { content: [{
type: "text",
text: jsonText(matched)
}] };
});
server.registerTool("wot_token", {
description: "Get component CSS variables.",
inputSchema: z.object({
component: z.string().optional(),
version: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ component, version: version$1 }) => {
if (!component) return { content: [{
type: "text",
text: jsonText({ components: listComponents(version$1).map((item) => ({
name: item.name,
cssVars: item.cssVars
})) })
}] };
const result = findComponent(component, version$1);
if (!result) return {
isError: true,
content: [{
type: "text",
text: `Component not found: ${component}`
}]
};
return { content: [{
type: "text",
text: jsonText({
name: result.name,
cssVars: result.cssVars
})
}] };
});
server.registerTool("wot_changelog", {
description: "Get changelog entries for the supported v2 dataset.",
inputSchema: z.object({
version: z.string().optional(),
component: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ version: version$1, component }) => {
return { content: [{
type: "text",
text: jsonText({ entries: (loadMetadataFile(resolveVersion(version$1)).changelog ?? []).filter((entry) => {
const versionMatches = version$1 ? entry.version === version$1 || `v${entry.version}` === version$1 : true;
const componentMatches = component ? (entry.components ?? []).some((item) => item.toLowerCase() === component.toLowerCase()) : true;
return versionMatches && componentMatches;
}) })
}] };
});
server.registerTool("wot_lint", {
description: "Lint a local project for wot-ui related issues.",
inputSchema: z.object({
dir: z.string().optional(),
version: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true
}
}, async ({ dir, version: version$1 }) => {
return { content: [{
type: "text",
text: jsonText(lintProject(dir ?? process.cwd(), version$1))
}] };
});
}
//#endregion
//#region src/mcp/server.ts
async function startMcpServer() {
const server = new McpServer({
name: "wot-ui",
version
}, {
instructions: "Use wot-ui component tools before generating UI code. Only wot-ui v2 metadata is available in this server.",
capabilities: { logging: {} }
});
registerMcpTools(server);
server.registerPrompt("wot-expert", { description: "General wot-ui expert workflow." }, async () => ({ messages: [{
role: "assistant",
content: {
type: "text",
text: WOT_EXPERT_PROMPT
}
}] }));
server.registerPrompt("wot-page-generator", {
description: "Workflow for generating a wot-ui page.",
argsSchema: z.object({ goal: z.string().optional() })
}, async ({ goal }) => ({ messages: [{
role: "assistant",
content: {
type: "text",
text: goal ? `${WOT_PAGE_GENERATOR_PROMPT} Goal: ${goal}` : WOT_PAGE_GENERATOR_PROMPT
}
}] }));
const transport = new StdioServerTransport();
await server.connect(transport);
const shutdown = async () => {
await server.close();
process.exit(0);
};
process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);
}
//#endregion
export { startMcpServer };
+1
-1

@@ -11,4 +11,4 @@ {

"v2": {
"2.0": "2.0.4"
"2.0": "2.0.8"
}
}
#!/usr/bin/env node
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-CcCQIYDr.mjs";
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";
import process from "node:process";

@@ -327,3 +327,3 @@ import { Command } from "commander";

program.command("mcp").description("Start the wot-ui MCP server").action(async () => {
const { startMcpServer } = await import("./server-BiSLWBUo.mjs");
const { startMcpServer } = await import("./server-zoFZTrLt.mjs");
await startMcpServer();

@@ -330,0 +330,0 @@ });

{
"name": "@wot-ui/cli",
"type": "module",
"version": "1.0.0",
"version": "1.0.1",
"description": "面向 wot-ui 的 CLI、MCP 与数据提取工具集",

@@ -6,0 +6,0 @@ "license": "MIT",

import process from "node:process";
import { existsSync, readFileSync, readdirSync } from "node:fs";
import { dirname, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { gunzipSync } from "node:zlib";
import { parse } from "@vue/compiler-sfc";
//#region package.json
var version = "1.0.0";
//#endregion
//#region src/data/loader.ts
const currentDir = dirname(fileURLToPath(import.meta.url));
function resolveDataDir() {
const candidates = [
join(currentDir, "..", "data"),
join(currentDir, "..", "..", "data"),
join(currentDir, "data")
];
for (const candidate of candidates) if (existsSync(join(candidate, "versions.json")) || existsSync(join(candidate, "versions.json.gz"))) return candidate;
throw new Error("Unable to locate bundled data directory");
}
const dataDir = resolveDataDir();
function readJsonFile(baseName) {
const jsonPath = join(dataDir, `${baseName}.json`);
if (existsSync(jsonPath)) return JSON.parse(readFileSync(jsonPath, "utf8"));
const gzipPath = join(dataDir, `${baseName}.json.gz`);
if (existsSync(gzipPath)) {
const compressed = readFileSync(gzipPath);
return JSON.parse(gunzipSync(compressed).toString("utf8"));
}
throw new Error(`Data file not found for ${baseName}`);
}
function loadVersionsFile() {
return readJsonFile("versions");
}
function loadMetadataFile(versionKey) {
return readJsonFile(versionKey);
}
//#endregion
//#region src/data/version.ts
/** Strip semver range operators (^, ~, >=, >, <=, <, =, whitespace). */
function stripRange(ver) {
return ver.replace(/[\^~>=<\s]/g, "");
}
/**
* Returns all stable version strings for major key 'v2',
* sorted ascending by semver.
*/
function stableV2Versions() {
const map = loadVersionsFile().v2 ?? {};
return Object.values(map).filter((v) => !v.includes("-")).sort((a, b) => {
const pa = a.split(".").map(Number);
const pb = b.split(".").map(Number);
for (let i = 0; i < 3; i++) {
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
if (diff !== 0) return diff;
}
return 0;
});
}
/**
* Auto-detect the wot-ui version to use.
*
* Priority:
* 1. --version flag (flagVersion arg)
* 2. node_modules/@wot-ui/ui/package.json in cwd
* 3. package.json dependencies[@wot-ui/ui] in cwd
* 4. Fallback to latest stable version from versions.json
*/
function detectVersion(flagVersion, cwd) {
const dir = cwd ?? process.cwd();
if (flagVersion) return {
version: flagVersion,
source: "flag"
};
const nmPath = join(dir, "node_modules", "@wot-ui", "ui", "package.json");
if (existsSync(nmPath)) try {
const pkg = JSON.parse(readFileSync(nmPath, "utf8"));
if (pkg.version) return {
version: pkg.version,
source: "node_modules"
};
} catch {}
const pkgPath = join(dir, "package.json");
if (existsSync(pkgPath)) try {
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
const depVersion = pkg.dependencies?.["@wot-ui/ui"] ?? pkg.devDependencies?.["@wot-ui/ui"] ?? pkg.peerDependencies?.["@wot-ui/ui"];
if (depVersion) return {
version: stripRange(depVersion),
source: "package.json"
};
} catch {}
return {
version: stableV2Versions().at(-1) ?? "2.0.0",
source: "fallback"
};
}
/**
* Resolve a version string (from detectVersion or CLI flag) to a data file key.
*
* Examples:
* undefined / 'v2' → 'v2' (major alias, data/v2.json)
* 'latest' → 'v2.0.4' (latest stable snapshot)
* '2.0' → 'v2.0.4' (minor → lookup in versions.json)
* '2.0.4' → 'v2.0.4' (exact patch)
* '2.0.0-alpha.5' → 'v2.0.0-alpha.5' (pre-release exact)
*/
function resolveVersion(requested) {
if (!requested || requested === "v2") return "v2";
const normalized = requested.trim();
if (normalized === "latest") {
const latest = stableV2Versions().at(-1);
if (!latest) return "v2";
return `v${latest}`;
}
const map = loadVersionsFile().v2 ?? {};
if (/^\d+\.\d+$/.test(normalized)) {
const patch = map[normalized];
if (!patch) throw new Error(`Unsupported wot-ui version: ${requested}`);
return `v${patch}`;
}
if (/^\d+\.\d+\.\d+/.test(normalized)) {
if (normalized.split(".")[0] !== "2") throw new Error(`Unsupported wot-ui version: ${requested}`);
return `v${normalized}`;
}
throw new Error(`Unsupported wot-ui version: ${requested}`);
}
//#endregion
//#region src/data/metadata.ts
function loadResolvedMetadata(version$1) {
return loadMetadataFile(resolveVersion(version$1));
}
function listComponents(version$1) {
return loadResolvedMetadata(version$1).components;
}
function findComponent(name, version$1) {
const normalized = name.trim().toLowerCase();
return listComponents(version$1).find((component) => component.name.toLowerCase() === normalized || component.tag.toLowerCase() === normalized);
}
//#endregion
//#region src/utils/files.ts
const DEFAULT_IGNORES = new Set([
".git",
".idea",
".output",
".turbo",
".vscode",
"dist",
"build",
"coverage",
"node_modules"
]);
function walkFiles(rootDir, extensions) {
const results = [];
function visit(dir) {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (DEFAULT_IGNORES.has(entry.name)) continue;
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
visit(fullPath);
continue;
}
if (extensions.some((extension) => entry.name.endsWith(extension))) results.push(fullPath);
}
}
visit(rootDir);
return results;
}
function safeRelative(rootDir, filePath) {
return relative(rootDir, filePath) || ".";
}
//#endregion
//#region src/utils/scanner.ts
const IMPORT_RE = /from\s+['"]([^'"]*wot[^'"]*)['"]/g;
const TAG_RE = /<\s*(wd-[a-z0-9-]+)/gi;
const BUTTON_RE = /<wd-button\b([^>]*)>([\s\S]*?)<\/wd-button>|<wd-button\b([^>]*)\/>/gi;
function getLineNumber(source, index) {
return source.slice(0, index).split("\n").length;
}
function collectTemplateTags(content) {
const counts = /* @__PURE__ */ new Map();
for (const match of content.matchAll(TAG_RE)) {
const tag = match[1]?.toLowerCase();
if (!tag) continue;
counts.set(tag, (counts.get(tag) ?? 0) + 1);
}
return counts;
}
function collectImports(scriptContent) {
const imports = /* @__PURE__ */ new Set();
for (const match of scriptContent.matchAll(IMPORT_RE)) if (match[1]) imports.add(match[1]);
return [...imports];
}
function analyzeUsage(targetDir, version$1) {
const dir = resolve(targetDir);
const files = walkFiles(dir, [".vue"]);
const knownByTag = new Map(listComponents(version$1).map((component) => [component.tag.toLowerCase(), component]));
const usageMap = /* @__PURE__ */ new Map();
const imports = /* @__PURE__ */ new Set();
for (const file of files) {
const parsed = parse(readFileSync(file, "utf8"), { filename: file });
const template = parsed.descriptor.template?.content ?? "";
const script = [parsed.descriptor.script?.content ?? "", parsed.descriptor.scriptSetup?.content ?? ""].filter(Boolean).join("\n");
for (const item of collectImports(script)) imports.add(item);
for (const [tag, count] of collectTemplateTags(template)) {
const known = knownByTag.get(tag);
const key = known?.name ?? tag;
const existing = usageMap.get(key);
if (existing) {
existing.count += count;
if (!existing.files.includes(safeRelative(dir, file))) existing.files.push(safeRelative(dir, file));
continue;
}
usageMap.set(key, {
name: known?.name ?? tag,
tag,
count,
files: [safeRelative(dir, file)]
});
}
}
return {
scannedFiles: files.length,
components: [...usageMap.values()].sort((left, right) => right.count - left.count || left.name.localeCompare(right.name)),
imports: [...imports].sort()
};
}
function lintProject(targetDir, version$1) {
const dir = resolve(targetDir);
const files = walkFiles(dir, [".vue"]);
const issues = [];
for (const file of files) {
const template = parse(readFileSync(file, "utf8"), { filename: file }).descriptor.template?.content ?? "";
for (const match of template.matchAll(TAG_RE)) {
const tag = match[1]?.toLowerCase();
if (!tag) continue;
if (!findComponent(tag, version$1)) issues.push({
file: safeRelative(dir, file),
line: getLineNumber(template, match.index ?? 0),
rule: "unknown-component",
severity: "warning",
message: `Unknown wot-ui component tag: ${tag}`
});
}
for (const match of template.matchAll(BUTTON_RE)) {
const attrs = (match[1] ?? match[3] ?? "").trim();
const body = (match[2] ?? "").replace(/<[^>]+>/g, "").trim();
if (!/\bicon\s*=/.test(attrs) && !body) issues.push({
file: safeRelative(dir, file),
line: getLineNumber(template, match.index ?? 0),
rule: "button-content",
severity: "warning",
message: "wd-button should include visible text content or an icon attribute."
});
const component = findComponent("wd-button", version$1);
for (const prop of component?.props ?? []) {
if (!prop.deprecated) continue;
if (!(/* @__PURE__ */ new RegExp(`\\b${prop.name}\\b`)).test(attrs)) continue;
issues.push({
file: safeRelative(dir, file),
line: getLineNumber(template, match.index ?? 0),
rule: "deprecated-prop",
severity: "warning",
message: prop.replacement ? `Deprecated prop ${prop.name} detected on wd-button. Use ${prop.replacement} instead.` : `Deprecated prop ${prop.name} detected on wd-button.`
});
}
}
}
return {
scannedFiles: files.length,
issues
};
}
//#endregion
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 };
import { c as version, i as listComponents, n as lintProject, o as resolveVersion, r as findComponent, s as loadMetadataFile } from "./scanner-CcCQIYDr.mjs";
import process from "node:process";
import { McpServer, StdioServerTransport } from "@modelcontextprotocol/server";
import * as z from "zod/v4";
//#region src/mcp/prompts.ts
const WOT_EXPERT_PROMPT = [
"You are a wot-ui expert assistant.",
"Always query component metadata before generating code.",
"Prefer using wot_list, wot_info, wot_doc, and wot_token before writing UI code.",
"Assume only wot-ui v2 is supported by this server."
].join(" ");
const WOT_PAGE_GENERATOR_PROMPT = [
"Generate wot-ui pages by first collecting every relevant component API and CSS variable.",
"Prefer existing wd-* components and documented props over ad-hoc custom markup.",
"When theme customization is involved, inspect CSS variables with wot_token first."
].join(" ");
//#endregion
//#region src/mcp/tools.ts
function jsonText(value) {
return JSON.stringify(value, null, 2);
}
function registerMcpTools(server) {
server.registerTool("wot_list", {
description: "List available wot-ui components.",
inputSchema: z.object({ version: z.string().optional() }),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ version: version$1 }) => {
return { content: [{
type: "text",
text: jsonText({ components: listComponents(version$1) })
}] };
});
server.registerTool("wot_info", {
description: "Get props, events, slots, and CSS variables for a component.",
inputSchema: z.object({
component: z.string(),
version: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ component, version: version$1 }) => {
const result = findComponent(component, version$1);
if (!result) return {
isError: true,
content: [{
type: "text",
text: `Component not found: ${component}`
}]
};
return { content: [{
type: "text",
text: jsonText(result)
}] };
});
server.registerTool("wot_doc", {
description: "Get component markdown documentation.",
inputSchema: z.object({
component: z.string(),
version: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ component, version: version$1 }) => {
const result = findComponent(component, version$1);
if (!result?.doc) return {
isError: true,
content: [{
type: "text",
text: `Documentation not found: ${component}`
}]
};
return { content: [{
type: "text",
text: result.doc
}] };
});
server.registerTool("wot_demo", {
description: "Get component demo code or list demos.",
inputSchema: z.object({
component: z.string(),
demo: z.string().optional(),
version: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ component, demo, version: version$1 }) => {
const result = findComponent(component, version$1);
if (!result) return {
isError: true,
content: [{
type: "text",
text: `Component not found: ${component}`
}]
};
if (!demo) return { content: [{
type: "text",
text: jsonText({ demos: result.demos ?? [] })
}] };
const matched = result.demos?.find((item) => item.name.toLowerCase() === demo.toLowerCase());
if (!matched) return {
isError: true,
content: [{
type: "text",
text: `Demo not found: ${demo}`
}]
};
return { content: [{
type: "text",
text: jsonText(matched)
}] };
});
server.registerTool("wot_token", {
description: "Get component CSS variables.",
inputSchema: z.object({
component: z.string().optional(),
version: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ component, version: version$1 }) => {
if (!component) return { content: [{
type: "text",
text: jsonText({ components: listComponents(version$1).map((item) => ({
name: item.name,
cssVars: item.cssVars
})) })
}] };
const result = findComponent(component, version$1);
if (!result) return {
isError: true,
content: [{
type: "text",
text: `Component not found: ${component}`
}]
};
return { content: [{
type: "text",
text: jsonText({
name: result.name,
cssVars: result.cssVars
})
}] };
});
server.registerTool("wot_changelog", {
description: "Get changelog entries for the supported v2 dataset.",
inputSchema: z.object({
version: z.string().optional(),
component: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
}, async ({ version: version$1, component }) => {
return { content: [{
type: "text",
text: jsonText({ entries: (loadMetadataFile(resolveVersion(version$1)).changelog ?? []).filter((entry) => {
const versionMatches = version$1 ? entry.version === version$1 || `v${entry.version}` === version$1 : true;
const componentMatches = component ? (entry.components ?? []).some((item) => item.toLowerCase() === component.toLowerCase()) : true;
return versionMatches && componentMatches;
}) })
}] };
});
server.registerTool("wot_lint", {
description: "Lint a local project for wot-ui related issues.",
inputSchema: z.object({
dir: z.string().optional(),
version: z.string().optional()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true
}
}, async ({ dir, version: version$1 }) => {
return { content: [{
type: "text",
text: jsonText(lintProject(dir ?? process.cwd(), version$1))
}] };
});
}
//#endregion
//#region src/mcp/server.ts
async function startMcpServer() {
const server = new McpServer({
name: "wot-ui",
version
}, {
instructions: "Use wot-ui component tools before generating UI code. Only wot-ui v2 metadata is available in this server.",
capabilities: { logging: {} }
});
registerMcpTools(server);
server.registerPrompt("wot-expert", { description: "General wot-ui expert workflow." }, async () => ({ messages: [{
role: "assistant",
content: {
type: "text",
text: WOT_EXPERT_PROMPT
}
}] }));
server.registerPrompt("wot-page-generator", {
description: "Workflow for generating a wot-ui page.",
argsSchema: z.object({ goal: z.string().optional() })
}, async ({ goal }) => ({ messages: [{
role: "assistant",
content: {
type: "text",
text: goal ? `${WOT_PAGE_GENERATOR_PROMPT} Goal: ${goal}` : WOT_PAGE_GENERATOR_PROMPT
}
}] }));
const transport = new StdioServerTransport();
await server.connect(transport);
const shutdown = async () => {
await server.close();
process.exit(0);
};
process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);
}
//#endregion
export { startMcpServer };

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet