uilint-core 0.2.145 → 0.2.147
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node.d.ts +1 -1
- package/dist/node.js +17 -17
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
package/dist/node.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export { default as pc } from 'picocolors';
|
|
|
10
10
|
* - Ensure the Ollama daemon is running (best effort: start `ollama serve` detached).
|
|
11
11
|
* - Ensure a given model is pulled (best effort: run `ollama pull <model>`).
|
|
12
12
|
*/
|
|
13
|
-
declare function isOllamaInstalled(): boolean
|
|
13
|
+
declare function isOllamaInstalled(): Promise<boolean>;
|
|
14
14
|
declare function ensureOllamaInstalledOrExplain(): Promise<void>;
|
|
15
15
|
declare function ensureOllamaRunning(options?: {
|
|
16
16
|
timeoutMs?: number;
|
package/dist/node.js
CHANGED
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
} from "./chunk-J4LUG7RV.js";
|
|
59
59
|
|
|
60
60
|
// src/ollama/bootstrap.ts
|
|
61
|
-
import { spawn
|
|
61
|
+
import { spawn } from "child_process";
|
|
62
62
|
import readline from "readline";
|
|
63
63
|
var DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434";
|
|
64
64
|
function sleep(ms) {
|
|
@@ -80,19 +80,19 @@ async function promptYesNo(question) {
|
|
|
80
80
|
});
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
|
-
function isOllamaInstalled() {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
async function isOllamaInstalled() {
|
|
84
|
+
return new Promise((resolve) => {
|
|
85
|
+
const child = spawn("ollama", ["--version"], { stdio: "ignore" });
|
|
86
|
+
child.on("error", () => resolve(false));
|
|
87
|
+
child.on("exit", (code) => resolve(code === 0));
|
|
88
|
+
});
|
|
89
89
|
}
|
|
90
|
-
function isBrewInstalled() {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
async function isBrewInstalled() {
|
|
91
|
+
return new Promise((resolve) => {
|
|
92
|
+
const child = spawn("brew", ["--version"], { stdio: "ignore" });
|
|
93
|
+
child.on("error", () => resolve(false));
|
|
94
|
+
child.on("exit", (code) => resolve(code === 0));
|
|
95
|
+
});
|
|
96
96
|
}
|
|
97
97
|
function getInstallInstructions() {
|
|
98
98
|
const lines = [
|
|
@@ -112,7 +112,7 @@ function getInstallInstructions() {
|
|
|
112
112
|
async function maybeInstallOllamaWithBrew() {
|
|
113
113
|
if (process.platform !== "darwin") return false;
|
|
114
114
|
if (!isInteractiveTTY()) return false;
|
|
115
|
-
if (!isBrewInstalled()) {
|
|
115
|
+
if (!await isBrewInstalled()) {
|
|
116
116
|
return false;
|
|
117
117
|
}
|
|
118
118
|
const ok = await promptYesNo(
|
|
@@ -127,14 +127,14 @@ async function maybeInstallOllamaWithBrew() {
|
|
|
127
127
|
else reject(new Error(`brew install ollama failed (exit ${code})`));
|
|
128
128
|
});
|
|
129
129
|
});
|
|
130
|
-
return isOllamaInstalled();
|
|
130
|
+
return await isOllamaInstalled();
|
|
131
131
|
}
|
|
132
132
|
async function ensureOllamaInstalledOrExplain() {
|
|
133
|
-
if (isOllamaInstalled()) return;
|
|
133
|
+
if (await isOllamaInstalled()) return;
|
|
134
134
|
const installedViaBrew = await maybeInstallOllamaWithBrew();
|
|
135
135
|
if (installedViaBrew) return;
|
|
136
136
|
const extra = [];
|
|
137
|
-
if (process.platform === "darwin" && !isBrewInstalled()) {
|
|
137
|
+
if (process.platform === "darwin" && !await isBrewInstalled()) {
|
|
138
138
|
extra.push("");
|
|
139
139
|
extra.push("Homebrew is not installed. Install it first, then run:");
|
|
140
140
|
extra.push(" brew install ollama");
|
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ollama/bootstrap.ts","../src/scanner/html-parser.ts","../src/styleguide/reader.ts","../src/utils/workspace-root.ts","../src/tailwind/config-reader.ts"],"sourcesContent":["/**\n * Ollama bootstrapping utilities (Node.js only).\n *\n * Goals:\n * - Detect whether Ollama is installed.\n * - Ensure the Ollama daemon is running (best effort: start `ollama serve` detached).\n * - Ensure a given model is pulled (best effort: run `ollama pull <model>`).\n */\n\nimport { spawn, spawnSync } from \"child_process\";\nimport readline from \"readline\";\nimport { OllamaClient } from \"./client.js\";\nimport { UILINT_DEFAULT_OLLAMA_MODEL } from \"./defaults.js\";\n\nconst DEFAULT_OLLAMA_BASE_URL = \"http://localhost:11434\";\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction isInteractiveTTY(): boolean {\n return Boolean(process.stdin.isTTY && process.stdout.isTTY);\n}\n\nasync function promptYesNo(question: string): Promise<boolean> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n return await new Promise<boolean>((resolve) => {\n rl.question(`${question} (y/N) `, (answer) => {\n rl.close();\n const normalized = (answer || \"\").trim().toLowerCase();\n resolve(normalized === \"y\" || normalized === \"yes\");\n });\n });\n}\n\nexport function isOllamaInstalled(): boolean {\n const result = spawnSync(\"ollama\", [\"--version\"], { stdio: \"ignore\" });\n if (\n result.error &&\n (result.error as NodeJS.ErrnoException).code === \"ENOENT\"\n ) {\n return false;\n }\n return result.status === 0;\n}\n\nfunction isBrewInstalled(): boolean {\n const result = spawnSync(\"brew\", [\"--version\"], { stdio: \"ignore\" });\n if (\n result.error &&\n (result.error as NodeJS.ErrnoException).code === \"ENOENT\"\n ) {\n return false;\n }\n return result.status === 0;\n}\n\nfunction getInstallInstructions(): string[] {\n const lines: string[] = [\n \"Ollama is required for LLM-backed analysis.\",\n \"\",\n \"Install Ollama:\",\n \" - Download: https://ollama.ai\",\n ];\n\n if (process.platform === \"darwin\") {\n lines.push(\" - Homebrew: brew install ollama\");\n }\n\n lines.push(\"\");\n lines.push(\"Then start it:\");\n lines.push(\" ollama serve\");\n return lines;\n}\n\nasync function maybeInstallOllamaWithBrew(): Promise<boolean> {\n if (process.platform !== \"darwin\") return false;\n if (!isInteractiveTTY()) return false;\n\n if (!isBrewInstalled()) {\n // We can't auto-install without brew; leave instructions to the caller.\n return false;\n }\n\n const ok = await promptYesNo(\n \"Ollama is not installed. Install with Homebrew now?\"\n );\n if (!ok) return false;\n\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\"brew\", [\"install\", \"ollama\"], { stdio: \"inherit\" });\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`brew install ollama failed (exit ${code})`));\n });\n });\n\n return isOllamaInstalled();\n}\n\nexport async function ensureOllamaInstalledOrExplain(): Promise<void> {\n if (isOllamaInstalled()) return;\n\n const installedViaBrew = await maybeInstallOllamaWithBrew();\n if (installedViaBrew) return;\n\n const extra: string[] = [];\n if (process.platform === \"darwin\" && !isBrewInstalled()) {\n extra.push(\"\");\n extra.push(\"Homebrew is not installed. Install it first, then run:\");\n extra.push(\" brew install ollama\");\n }\n\n throw new Error([...getInstallInstructions(), ...extra].join(\"\\n\"));\n}\n\nexport async function ensureOllamaRunning(options?: {\n timeoutMs?: number;\n baseUrl?: string;\n}): Promise<void> {\n await ensureOllamaInstalledOrExplain();\n\n const baseUrl = options?.baseUrl || DEFAULT_OLLAMA_BASE_URL;\n const client = new OllamaClient({ baseUrl });\n const timeoutMs = options?.timeoutMs ?? 10_000;\n\n if (await client.isAvailable()) return;\n\n // Best-effort background start. We do not stop it later.\n try {\n const child = spawn(\"ollama\", [\"serve\"], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n } catch (err) {\n throw new Error(\n `Failed to start Ollama automatically.\\n\\n${getInstallInstructions().join(\n \"\\n\"\n )}\\n\\nDetails: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n if (await client.isAvailable()) return;\n await sleep(250);\n }\n\n throw new Error(\n [\n \"Ollama did not become ready in time.\",\n \"\",\n \"Try starting it manually:\",\n \" ollama serve\",\n ].join(\"\\n\")\n );\n}\n\nasync function fetchOllamaTags(baseUrl: string): Promise<string[]> {\n const res = await fetch(`${baseUrl}/api/tags`, {\n method: \"GET\",\n signal: AbortSignal.timeout(5000),\n });\n\n if (!res.ok) {\n throw new Error(`Ollama tags endpoint returned ${res.status}`);\n }\n\n const data = (await res.json()) as { models?: Array<{ name?: string }> };\n const names = (data.models ?? [])\n .map((m) => m.name)\n .filter((n): n is string => typeof n === \"string\");\n return names;\n}\n\nexport async function ensureOllamaModelPulled(options?: {\n model?: string;\n baseUrl?: string;\n}): Promise<void> {\n const desired = (options?.model || UILINT_DEFAULT_OLLAMA_MODEL).trim();\n if (!desired) return;\n\n const baseUrl = options?.baseUrl || DEFAULT_OLLAMA_BASE_URL;\n const tags = await fetchOllamaTags(baseUrl);\n if (tags.includes(desired)) return;\n\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\"ollama\", [\"pull\", desired], { stdio: \"inherit\" });\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`ollama pull ${desired} failed (exit ${code})`));\n });\n });\n\n const tagsAfter = await fetchOllamaTags(baseUrl);\n if (!tagsAfter.includes(desired)) {\n throw new Error(\n `Model ${desired} did not appear in Ollama tags after pulling.`\n );\n }\n}\n\nexport async function ensureOllamaReady(options?: {\n model?: string;\n timeoutMs?: number;\n baseUrl?: string;\n}): Promise<void> {\n await ensureOllamaRunning({\n timeoutMs: options?.timeoutMs,\n baseUrl: options?.baseUrl,\n });\n await ensureOllamaModelPulled({\n model: options?.model,\n baseUrl: options?.baseUrl,\n });\n}\n","/**\n * HTML parser for extracting styles using JSDOM\n * Used by CLI and Node.js environments\n */\n\nimport { JSDOM } from \"jsdom\";\nimport type { DOMSnapshot, SerializedStyles } from \"../types.js\";\nimport { extractStyles, deserializeStyles, truncateHTML } from \"./style-extractor.js\";\n\nexport interface ParseOptions {\n /**\n * If true, tries to load and process linked stylesheets\n */\n loadStylesheets?: boolean;\n /**\n * Max length for HTML in snapshot\n */\n maxHtmlLength?: number;\n}\n\n/**\n * Parses raw HTML and extracts styles using JSDOM\n */\nexport function parseHTML(\n html: string,\n options: ParseOptions = {}\n): DOMSnapshot {\n const { maxHtmlLength = 50000 } = options;\n\n const dom = new JSDOM(html, {\n runScripts: \"outside-only\",\n pretendToBeVisual: true,\n });\n\n const { document, getComputedStyle } = dom.window;\n const root = document.body || document.documentElement;\n\n const styles = extractStyles(root, getComputedStyle);\n const elementCount = root.querySelectorAll(\"*\").length;\n\n return {\n html: truncateHTML(html, maxHtmlLength),\n styles,\n elementCount,\n timestamp: Date.now(),\n };\n}\n\n/**\n * Input format for CLI - can be raw HTML or pre-extracted JSON\n */\nexport interface CLIInput {\n html: string;\n styles?: SerializedStyles;\n}\n\n/**\n * Parses CLI input which can be either raw HTML or JSON with pre-extracted styles\n */\nexport function parseCLIInput(input: string): DOMSnapshot {\n // Try to parse as JSON first\n try {\n const parsed = JSON.parse(input) as CLIInput;\n \n if (parsed.html && parsed.styles) {\n // Pre-extracted styles from browser/test environment\n return {\n html: truncateHTML(parsed.html),\n styles: deserializeStyles(parsed.styles),\n elementCount: 0, // Not available for pre-extracted\n timestamp: Date.now(),\n };\n } else if (parsed.html) {\n // Just HTML in JSON format\n return parseHTML(parsed.html);\n }\n } catch {\n // Not JSON, treat as raw HTML\n }\n\n // Parse as raw HTML\n return parseHTML(input);\n}\n\n/**\n * Checks if a string looks like JSON\n */\nexport function isJSON(str: string): boolean {\n const trimmed = str.trim();\n return trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\");\n}\n\n/**\n * Reads input from stdin\n */\nexport async function readStdin(): Promise<string> {\n const chunks: Uint8Array[] = [];\n \n return new Promise((resolve, reject) => {\n process.stdin.on(\"data\", (chunk: Buffer | string) => {\n chunks.push(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk);\n });\n process.stdin.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf-8\")));\n process.stdin.on(\"error\", reject);\n });\n}\n\n/**\n * Detects if stdin has data\n */\nexport function hasStdin(): boolean {\n return !process.stdin.isTTY;\n}\n\n","/**\n * Style guide file operations\n */\n\nimport { readFile, writeFile, mkdir } from \"fs/promises\";\nimport { existsSync } from \"fs\";\nimport { join, dirname } from \"path\";\n\nexport const STYLEGUIDE_PATHS = [\n \".uilint/styleguide.md\",\n \"styleguide.md\",\n \".uilint/style-guide.md\",\n];\n\n/**\n * Walk upward from a starting directory and look specifically for `.uilint/styleguide.md`.\n *\n * This is intended for flows where the \"project root\" is ambiguous (e.g., analyzing\n * an arbitrary file path) and we want the nearest `.uilint` config on the way up.\n */\nexport function findUILintStyleGuideUpwards(startDir: string): string | null {\n let dir = startDir;\n for (;;) {\n const candidate = join(dir, \".uilint\", \"styleguide.md\");\n if (existsSync(candidate)) return candidate;\n\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/**\n * Finds the style guide file in a project\n */\nexport function findStyleGuidePath(projectPath: string): string | null {\n for (const relativePath of STYLEGUIDE_PATHS) {\n const fullPath = join(projectPath, relativePath);\n if (existsSync(fullPath)) {\n return fullPath;\n }\n }\n return null;\n}\n\n/**\n * Reads the style guide content\n */\nexport async function readStyleGuide(path: string): Promise<string> {\n return readFile(path, \"utf-8\");\n}\n\n/**\n * Reads style guide from project path, finding it automatically\n */\nexport async function readStyleGuideFromProject(\n projectPath: string\n): Promise<string | null> {\n const path = findStyleGuidePath(projectPath);\n if (!path) return null;\n return readStyleGuide(path);\n}\n\n/**\n * Writes style guide content to file\n */\nexport async function writeStyleGuide(\n path: string,\n content: string\n): Promise<void> {\n const dir = dirname(path);\n await mkdir(dir, { recursive: true });\n await writeFile(path, content, \"utf-8\");\n}\n\n/**\n * Gets the default style guide path for a project\n */\nexport function getDefaultStyleGuidePath(projectPath: string): string {\n return join(projectPath, \".uilint\", \"styleguide.md\");\n}\n\n/**\n * Checks if a style guide exists\n */\nexport function styleGuideExists(projectPath: string): boolean {\n return findStyleGuidePath(projectPath) !== null;\n}\n","/**\n * Resolve the workspace/repo root from an arbitrary starting directory.\n *\n * This is primarily used in monorepos where runtime `process.cwd()` may point at\n * a sub-app (e.g. `apps/web`) but UI linting assets live at the workspace root.\n */\nimport { existsSync, readFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\n\nfunction hasWorkspacesField(dir: string): boolean {\n const pkgPath = join(dir, \"package.json\");\n if (!existsSync(pkgPath)) return false;\n try {\n const raw = readFileSync(pkgPath, \"utf-8\");\n const pkg = JSON.parse(raw) as { workspaces?: unknown };\n return !!pkg.workspaces;\n } catch {\n return false;\n }\n}\n\nfunction hasRootMarker(dir: string): boolean {\n // pnpm is the primary supported monorepo layout in this repo\n if (existsSync(join(dir, \"pnpm-workspace.yaml\"))) return true;\n // fallbacks for other setups\n if (existsSync(join(dir, \".git\"))) return true;\n if (hasWorkspacesField(dir)) return true;\n return false;\n}\n\n/**\n * Walks up from `startDir` to find a likely workspace root.\n *\n * If none is found, returns `startDir`.\n */\nexport function findWorkspaceRoot(startDir: string): string {\n let dir = startDir;\n let lastFallback: string | null = null;\n\n for (;;) {\n if (existsSync(join(dir, \"pnpm-workspace.yaml\"))) return dir;\n\n if (hasRootMarker(dir)) {\n // keep walking to prefer the highest-level marker (esp. .git)\n lastFallback = dir;\n }\n\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n return lastFallback || startDir;\n}\n","/**\n * Tailwind config reader (Node-only).\n *\n * Goals:\n * - Locate a Tailwind config file near a project directory.\n * - Load it (supports .ts via jiti).\n * - Produce compact token sets for styleguide + validation.\n *\n * Note: We intentionally extract tokens primarily from user-defined theme / extend\n * to avoid dumping Tailwind's full default palette into the style guide.\n */\n\nimport { existsSync } from \"fs\";\nimport { createRequire } from \"module\";\nimport { dirname, join } from \"path\";\nimport jiti from \"jiti\";\nimport type { TailwindThemeTokens } from \"../types.js\";\n\nconst CONFIG_CANDIDATES = [\n \"tailwind.config.ts\",\n \"tailwind.config.mts\",\n \"tailwind.config.cts\",\n \"tailwind.config.js\",\n \"tailwind.config.mjs\",\n \"tailwind.config.cjs\",\n];\n\nexport function findTailwindConfigPath(startDir: string): string | null {\n let dir = startDir;\n for (;;) {\n for (const name of CONFIG_CANDIDATES) {\n const full = join(dir, name);\n if (existsSync(full)) return full;\n }\n\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nexport function readTailwindThemeTokens(\n projectDir: string\n): TailwindThemeTokens | null {\n const configPath = findTailwindConfigPath(projectDir);\n if (!configPath) return null;\n\n const loader = jiti(import.meta.url, { interopDefault: true });\n const loaded = loader(configPath) as Record<string, unknown>;\n const config = ((loaded?.default ?? loaded) as Record<string, unknown>);\n if (!config || typeof config !== \"object\") return null;\n\n // Best-effort: run resolveConfig to ensure config is valid/normalized.\n // We don’t use the resolved theme for token enumeration (to avoid defaults),\n // but we do want to surface loader/shape problems early for debugging.\n try {\n const require = createRequire(import.meta.url);\n const maybe = require(\"tailwindcss/resolveConfig\");\n const resolveConfig = (maybe?.default ?? maybe) as\n | ((cfg: Record<string, unknown>) => unknown)\n | undefined;\n if (typeof resolveConfig === \"function\") resolveConfig(config);\n } catch {\n // If resolve fails, still attempt to extract from raw object.\n }\n\n const theme: Record<string, unknown> =\n config.theme && typeof config.theme === \"object\" ? (config.theme as Record<string, unknown>) : {};\n const extend: Record<string, unknown> =\n theme.extend && typeof theme.extend === \"object\" ? (theme.extend as Record<string, unknown>) : {};\n\n const colors = new Set<string>();\n const spacingKeys = new Set<string>();\n const borderRadiusKeys = new Set<string>();\n const fontFamilyKeys = new Set<string>();\n const fontSizeKeys = new Set<string>();\n\n // Merge base + extend per category.\n addColorTokens(colors, theme.colors);\n addColorTokens(colors, extend.colors);\n\n addKeys(spacingKeys, theme.spacing);\n addKeys(spacingKeys, extend.spacing);\n\n addKeys(borderRadiusKeys, theme.borderRadius);\n addKeys(borderRadiusKeys, extend.borderRadius);\n\n addKeys(fontFamilyKeys, theme.fontFamily);\n addKeys(fontFamilyKeys, extend.fontFamily);\n\n addKeys(fontSizeKeys, theme.fontSize);\n addKeys(fontSizeKeys, extend.fontSize);\n\n // If user config didn’t specify tokens, we still return an object to signal\n // \"tailwind detected\", but with empty sets (downstream can choose defaults).\n return {\n configPath,\n colors: [...colors],\n spacingKeys: [...spacingKeys],\n borderRadiusKeys: [...borderRadiusKeys],\n fontFamilyKeys: [...fontFamilyKeys],\n fontSizeKeys: [...fontSizeKeys],\n };\n}\n\nfunction addKeys(out: Set<string>, obj: unknown): void {\n if (!obj || typeof obj !== \"object\") return;\n for (const key of Object.keys(obj as Record<string, unknown>)) {\n if (!key) continue;\n out.add(key);\n }\n}\n\nfunction addColorTokens(out: Set<string>, colors: unknown): void {\n if (!colors || typeof colors !== \"object\") return;\n\n for (const [name, value] of Object.entries(\n colors as Record<string, unknown>\n )) {\n if (!name) continue;\n\n if (typeof value === \"string\") {\n out.add(`tailwind:${name}`);\n continue;\n }\n\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n for (const shade of Object.keys(value as Record<string, unknown>)) {\n if (!shade) continue;\n out.add(`tailwind:${name}-${shade}`);\n }\n continue;\n }\n\n // Arrays / functions etc are ignored for token enumeration.\n }\n}\n"],"mappings":"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","names":["existsSync","dirname","join","existsSync","dirname","join","require"]}
|
|
1
|
+
{"version":3,"sources":["../src/ollama/bootstrap.ts","../src/scanner/html-parser.ts","../src/styleguide/reader.ts","../src/utils/workspace-root.ts","../src/tailwind/config-reader.ts"],"sourcesContent":["/**\n * Ollama bootstrapping utilities (Node.js only).\n *\n * Goals:\n * - Detect whether Ollama is installed.\n * - Ensure the Ollama daemon is running (best effort: start `ollama serve` detached).\n * - Ensure a given model is pulled (best effort: run `ollama pull <model>`).\n */\n\nimport { spawn } from \"child_process\";\nimport readline from \"readline\";\nimport { OllamaClient } from \"./client.js\";\nimport { UILINT_DEFAULT_OLLAMA_MODEL } from \"./defaults.js\";\n\nconst DEFAULT_OLLAMA_BASE_URL = \"http://localhost:11434\";\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction isInteractiveTTY(): boolean {\n return Boolean(process.stdin.isTTY && process.stdout.isTTY);\n}\n\nasync function promptYesNo(question: string): Promise<boolean> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n return await new Promise<boolean>((resolve) => {\n rl.question(`${question} (y/N) `, (answer) => {\n rl.close();\n const normalized = (answer || \"\").trim().toLowerCase();\n resolve(normalized === \"y\" || normalized === \"yes\");\n });\n });\n}\n\nexport async function isOllamaInstalled(): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const child = spawn(\"ollama\", [\"--version\"], { stdio: \"ignore\" });\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => resolve(code === 0));\n });\n}\n\nasync function isBrewInstalled(): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const child = spawn(\"brew\", [\"--version\"], { stdio: \"ignore\" });\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => resolve(code === 0));\n });\n}\n\nfunction getInstallInstructions(): string[] {\n const lines: string[] = [\n \"Ollama is required for LLM-backed analysis.\",\n \"\",\n \"Install Ollama:\",\n \" - Download: https://ollama.ai\",\n ];\n\n if (process.platform === \"darwin\") {\n lines.push(\" - Homebrew: brew install ollama\");\n }\n\n lines.push(\"\");\n lines.push(\"Then start it:\");\n lines.push(\" ollama serve\");\n return lines;\n}\n\nasync function maybeInstallOllamaWithBrew(): Promise<boolean> {\n if (process.platform !== \"darwin\") return false;\n if (!isInteractiveTTY()) return false;\n\n if (!(await isBrewInstalled())) {\n // We can't auto-install without brew; leave instructions to the caller.\n return false;\n }\n\n const ok = await promptYesNo(\n \"Ollama is not installed. Install with Homebrew now?\"\n );\n if (!ok) return false;\n\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\"brew\", [\"install\", \"ollama\"], { stdio: \"inherit\" });\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`brew install ollama failed (exit ${code})`));\n });\n });\n\n return await isOllamaInstalled();\n}\n\nexport async function ensureOllamaInstalledOrExplain(): Promise<void> {\n if (await isOllamaInstalled()) return;\n\n const installedViaBrew = await maybeInstallOllamaWithBrew();\n if (installedViaBrew) return;\n\n const extra: string[] = [];\n if (process.platform === \"darwin\" && !(await isBrewInstalled())) {\n extra.push(\"\");\n extra.push(\"Homebrew is not installed. Install it first, then run:\");\n extra.push(\" brew install ollama\");\n }\n\n throw new Error([...getInstallInstructions(), ...extra].join(\"\\n\"));\n}\n\nexport async function ensureOllamaRunning(options?: {\n timeoutMs?: number;\n baseUrl?: string;\n}): Promise<void> {\n await ensureOllamaInstalledOrExplain();\n\n const baseUrl = options?.baseUrl || DEFAULT_OLLAMA_BASE_URL;\n const client = new OllamaClient({ baseUrl });\n const timeoutMs = options?.timeoutMs ?? 10_000;\n\n if (await client.isAvailable()) return;\n\n // Best-effort background start. We do not stop it later.\n try {\n const child = spawn(\"ollama\", [\"serve\"], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n } catch (err) {\n throw new Error(\n `Failed to start Ollama automatically.\\n\\n${getInstallInstructions().join(\n \"\\n\"\n )}\\n\\nDetails: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n if (await client.isAvailable()) return;\n await sleep(250);\n }\n\n throw new Error(\n [\n \"Ollama did not become ready in time.\",\n \"\",\n \"Try starting it manually:\",\n \" ollama serve\",\n ].join(\"\\n\")\n );\n}\n\nasync function fetchOllamaTags(baseUrl: string): Promise<string[]> {\n const res = await fetch(`${baseUrl}/api/tags`, {\n method: \"GET\",\n signal: AbortSignal.timeout(5000),\n });\n\n if (!res.ok) {\n throw new Error(`Ollama tags endpoint returned ${res.status}`);\n }\n\n const data = (await res.json()) as { models?: Array<{ name?: string }> };\n const names = (data.models ?? [])\n .map((m) => m.name)\n .filter((n): n is string => typeof n === \"string\");\n return names;\n}\n\nexport async function ensureOllamaModelPulled(options?: {\n model?: string;\n baseUrl?: string;\n}): Promise<void> {\n const desired = (options?.model || UILINT_DEFAULT_OLLAMA_MODEL).trim();\n if (!desired) return;\n\n const baseUrl = options?.baseUrl || DEFAULT_OLLAMA_BASE_URL;\n const tags = await fetchOllamaTags(baseUrl);\n if (tags.includes(desired)) return;\n\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\"ollama\", [\"pull\", desired], { stdio: \"inherit\" });\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`ollama pull ${desired} failed (exit ${code})`));\n });\n });\n\n const tagsAfter = await fetchOllamaTags(baseUrl);\n if (!tagsAfter.includes(desired)) {\n throw new Error(\n `Model ${desired} did not appear in Ollama tags after pulling.`\n );\n }\n}\n\nexport async function ensureOllamaReady(options?: {\n model?: string;\n timeoutMs?: number;\n baseUrl?: string;\n}): Promise<void> {\n await ensureOllamaRunning({\n timeoutMs: options?.timeoutMs,\n baseUrl: options?.baseUrl,\n });\n await ensureOllamaModelPulled({\n model: options?.model,\n baseUrl: options?.baseUrl,\n });\n}\n","/**\n * HTML parser for extracting styles using JSDOM\n * Used by CLI and Node.js environments\n */\n\nimport { JSDOM } from \"jsdom\";\nimport type { DOMSnapshot, SerializedStyles } from \"../types.js\";\nimport { extractStyles, deserializeStyles, truncateHTML } from \"./style-extractor.js\";\n\nexport interface ParseOptions {\n /**\n * If true, tries to load and process linked stylesheets\n */\n loadStylesheets?: boolean;\n /**\n * Max length for HTML in snapshot\n */\n maxHtmlLength?: number;\n}\n\n/**\n * Parses raw HTML and extracts styles using JSDOM\n */\nexport function parseHTML(\n html: string,\n options: ParseOptions = {}\n): DOMSnapshot {\n const { maxHtmlLength = 50000 } = options;\n\n const dom = new JSDOM(html, {\n runScripts: \"outside-only\",\n pretendToBeVisual: true,\n });\n\n const { document, getComputedStyle } = dom.window;\n const root = document.body || document.documentElement;\n\n const styles = extractStyles(root, getComputedStyle);\n const elementCount = root.querySelectorAll(\"*\").length;\n\n return {\n html: truncateHTML(html, maxHtmlLength),\n styles,\n elementCount,\n timestamp: Date.now(),\n };\n}\n\n/**\n * Input format for CLI - can be raw HTML or pre-extracted JSON\n */\nexport interface CLIInput {\n html: string;\n styles?: SerializedStyles;\n}\n\n/**\n * Parses CLI input which can be either raw HTML or JSON with pre-extracted styles\n */\nexport function parseCLIInput(input: string): DOMSnapshot {\n // Try to parse as JSON first\n try {\n const parsed = JSON.parse(input) as CLIInput;\n \n if (parsed.html && parsed.styles) {\n // Pre-extracted styles from browser/test environment\n return {\n html: truncateHTML(parsed.html),\n styles: deserializeStyles(parsed.styles),\n elementCount: 0, // Not available for pre-extracted\n timestamp: Date.now(),\n };\n } else if (parsed.html) {\n // Just HTML in JSON format\n return parseHTML(parsed.html);\n }\n } catch {\n // Not JSON, treat as raw HTML\n }\n\n // Parse as raw HTML\n return parseHTML(input);\n}\n\n/**\n * Checks if a string looks like JSON\n */\nexport function isJSON(str: string): boolean {\n const trimmed = str.trim();\n return trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\");\n}\n\n/**\n * Reads input from stdin\n */\nexport async function readStdin(): Promise<string> {\n const chunks: Uint8Array[] = [];\n \n return new Promise((resolve, reject) => {\n process.stdin.on(\"data\", (chunk: Buffer | string) => {\n chunks.push(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk);\n });\n process.stdin.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf-8\")));\n process.stdin.on(\"error\", reject);\n });\n}\n\n/**\n * Detects if stdin has data\n */\nexport function hasStdin(): boolean {\n return !process.stdin.isTTY;\n}\n\n","/**\n * Style guide file operations\n */\n\nimport { readFile, writeFile, mkdir } from \"fs/promises\";\nimport { existsSync } from \"fs\";\nimport { join, dirname } from \"path\";\n\nexport const STYLEGUIDE_PATHS = [\n \".uilint/styleguide.md\",\n \"styleguide.md\",\n \".uilint/style-guide.md\",\n];\n\n/**\n * Walk upward from a starting directory and look specifically for `.uilint/styleguide.md`.\n *\n * This is intended for flows where the \"project root\" is ambiguous (e.g., analyzing\n * an arbitrary file path) and we want the nearest `.uilint` config on the way up.\n */\nexport function findUILintStyleGuideUpwards(startDir: string): string | null {\n let dir = startDir;\n for (;;) {\n const candidate = join(dir, \".uilint\", \"styleguide.md\");\n if (existsSync(candidate)) return candidate;\n\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/**\n * Finds the style guide file in a project\n */\nexport function findStyleGuidePath(projectPath: string): string | null {\n for (const relativePath of STYLEGUIDE_PATHS) {\n const fullPath = join(projectPath, relativePath);\n if (existsSync(fullPath)) {\n return fullPath;\n }\n }\n return null;\n}\n\n/**\n * Reads the style guide content\n */\nexport async function readStyleGuide(path: string): Promise<string> {\n return readFile(path, \"utf-8\");\n}\n\n/**\n * Reads style guide from project path, finding it automatically\n */\nexport async function readStyleGuideFromProject(\n projectPath: string\n): Promise<string | null> {\n const path = findStyleGuidePath(projectPath);\n if (!path) return null;\n return readStyleGuide(path);\n}\n\n/**\n * Writes style guide content to file\n */\nexport async function writeStyleGuide(\n path: string,\n content: string\n): Promise<void> {\n const dir = dirname(path);\n await mkdir(dir, { recursive: true });\n await writeFile(path, content, \"utf-8\");\n}\n\n/**\n * Gets the default style guide path for a project\n */\nexport function getDefaultStyleGuidePath(projectPath: string): string {\n return join(projectPath, \".uilint\", \"styleguide.md\");\n}\n\n/**\n * Checks if a style guide exists\n */\nexport function styleGuideExists(projectPath: string): boolean {\n return findStyleGuidePath(projectPath) !== null;\n}\n","/**\n * Resolve the workspace/repo root from an arbitrary starting directory.\n *\n * This is primarily used in monorepos where runtime `process.cwd()` may point at\n * a sub-app (e.g. `apps/web`) but UI linting assets live at the workspace root.\n */\nimport { existsSync, readFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\n\nfunction hasWorkspacesField(dir: string): boolean {\n const pkgPath = join(dir, \"package.json\");\n if (!existsSync(pkgPath)) return false;\n try {\n const raw = readFileSync(pkgPath, \"utf-8\");\n const pkg = JSON.parse(raw) as { workspaces?: unknown };\n return !!pkg.workspaces;\n } catch {\n return false;\n }\n}\n\nfunction hasRootMarker(dir: string): boolean {\n // pnpm is the primary supported monorepo layout in this repo\n if (existsSync(join(dir, \"pnpm-workspace.yaml\"))) return true;\n // fallbacks for other setups\n if (existsSync(join(dir, \".git\"))) return true;\n if (hasWorkspacesField(dir)) return true;\n return false;\n}\n\n/**\n * Walks up from `startDir` to find a likely workspace root.\n *\n * If none is found, returns `startDir`.\n */\nexport function findWorkspaceRoot(startDir: string): string {\n let dir = startDir;\n let lastFallback: string | null = null;\n\n for (;;) {\n if (existsSync(join(dir, \"pnpm-workspace.yaml\"))) return dir;\n\n if (hasRootMarker(dir)) {\n // keep walking to prefer the highest-level marker (esp. .git)\n lastFallback = dir;\n }\n\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n return lastFallback || startDir;\n}\n","/**\n * Tailwind config reader (Node-only).\n *\n * Goals:\n * - Locate a Tailwind config file near a project directory.\n * - Load it (supports .ts via jiti).\n * - Produce compact token sets for styleguide + validation.\n *\n * Note: We intentionally extract tokens primarily from user-defined theme / extend\n * to avoid dumping Tailwind's full default palette into the style guide.\n */\n\nimport { existsSync } from \"fs\";\nimport { createRequire } from \"module\";\nimport { dirname, join } from \"path\";\nimport jiti from \"jiti\";\nimport type { TailwindThemeTokens } from \"../types.js\";\n\nconst CONFIG_CANDIDATES = [\n \"tailwind.config.ts\",\n \"tailwind.config.mts\",\n \"tailwind.config.cts\",\n \"tailwind.config.js\",\n \"tailwind.config.mjs\",\n \"tailwind.config.cjs\",\n];\n\nexport function findTailwindConfigPath(startDir: string): string | null {\n let dir = startDir;\n for (;;) {\n for (const name of CONFIG_CANDIDATES) {\n const full = join(dir, name);\n if (existsSync(full)) return full;\n }\n\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nexport function readTailwindThemeTokens(\n projectDir: string\n): TailwindThemeTokens | null {\n const configPath = findTailwindConfigPath(projectDir);\n if (!configPath) return null;\n\n const loader = jiti(import.meta.url, { interopDefault: true });\n const loaded = loader(configPath) as Record<string, unknown>;\n const config = ((loaded?.default ?? loaded) as Record<string, unknown>);\n if (!config || typeof config !== \"object\") return null;\n\n // Best-effort: run resolveConfig to ensure config is valid/normalized.\n // We don’t use the resolved theme for token enumeration (to avoid defaults),\n // but we do want to surface loader/shape problems early for debugging.\n try {\n const require = createRequire(import.meta.url);\n const maybe = require(\"tailwindcss/resolveConfig\");\n const resolveConfig = (maybe?.default ?? maybe) as\n | ((cfg: Record<string, unknown>) => unknown)\n | undefined;\n if (typeof resolveConfig === \"function\") resolveConfig(config);\n } catch {\n // If resolve fails, still attempt to extract from raw object.\n }\n\n const theme: Record<string, unknown> =\n config.theme && typeof config.theme === \"object\" ? (config.theme as Record<string, unknown>) : {};\n const extend: Record<string, unknown> =\n theme.extend && typeof theme.extend === \"object\" ? (theme.extend as Record<string, unknown>) : {};\n\n const colors = new Set<string>();\n const spacingKeys = new Set<string>();\n const borderRadiusKeys = new Set<string>();\n const fontFamilyKeys = new Set<string>();\n const fontSizeKeys = new Set<string>();\n\n // Merge base + extend per category.\n addColorTokens(colors, theme.colors);\n addColorTokens(colors, extend.colors);\n\n addKeys(spacingKeys, theme.spacing);\n addKeys(spacingKeys, extend.spacing);\n\n addKeys(borderRadiusKeys, theme.borderRadius);\n addKeys(borderRadiusKeys, extend.borderRadius);\n\n addKeys(fontFamilyKeys, theme.fontFamily);\n addKeys(fontFamilyKeys, extend.fontFamily);\n\n addKeys(fontSizeKeys, theme.fontSize);\n addKeys(fontSizeKeys, extend.fontSize);\n\n // If user config didn’t specify tokens, we still return an object to signal\n // \"tailwind detected\", but with empty sets (downstream can choose defaults).\n return {\n configPath,\n colors: [...colors],\n spacingKeys: [...spacingKeys],\n borderRadiusKeys: [...borderRadiusKeys],\n fontFamilyKeys: [...fontFamilyKeys],\n fontSizeKeys: [...fontSizeKeys],\n };\n}\n\nfunction addKeys(out: Set<string>, obj: unknown): void {\n if (!obj || typeof obj !== \"object\") return;\n for (const key of Object.keys(obj as Record<string, unknown>)) {\n if (!key) continue;\n out.add(key);\n }\n}\n\nfunction addColorTokens(out: Set<string>, colors: unknown): void {\n if (!colors || typeof colors !== \"object\") return;\n\n for (const [name, value] of Object.entries(\n colors as Record<string, unknown>\n )) {\n if (!name) continue;\n\n if (typeof value === \"string\") {\n out.add(`tailwind:${name}`);\n continue;\n }\n\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n for (const shade of Object.keys(value as Record<string, unknown>)) {\n if (!shade) continue;\n out.add(`tailwind:${name}-${shade}`);\n }\n continue;\n }\n\n // Arrays / functions etc are ignored for token enumeration.\n }\n}\n"],"mappings":"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","names":["existsSync","dirname","join","existsSync","dirname","join","require"]}
|