uilint-core 0.1.32 → 0.1.34

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 CHANGED
@@ -1,5 +1,41 @@
1
1
  import { DOMSnapshot, TailwindThemeTokens } from './index.js';
2
2
  export { AnalysisResult, AnalyzeConsistencyOptions, ColorRule, ComponentRule, ConsistencyResult, ElementRole, ElementSnapshot, ExtractedStyleValues, ExtractedStyles, GroupedSnapshot, InstrumentationSpan, LLMInstrumentationCallbacks, OllamaClient, OllamaClientOptions, SerializedStyles, SpacingRule, StreamProgressCallback, StyleGuide, StyleSnapshot, TypographyRule, UILINT_DEFAULT_OLLAMA_MODEL, UILintIssue, UILintScanIssue, UILintSourceScanResult, Violation, ViolationCategory, ViolationSeverity, analyzeConsistency, buildAnalysisPrompt, buildConsistencyPrompt, buildSourceAnalysisPrompt, buildSourceScanPrompt, buildStyleGuidePrompt, countElements, createColorRule, createComponentRule, createEmptyStyleGuide, createSpacingRule, createStyleSummary, createTypographyRule, deserializeStyles, extractStyleValues, extractStyles, extractStylesFromDOM, extractTailwindAllowlist, formatConsistencyViolations, formatViolationsText, generateStyleGuideFromStyles, getOllamaClient, hasAnalyzableGroups, mergeStyleGuides, parseGroupedSnapshot, parseStyleGuide, parseStyleGuideSections, parseViolationsResponse, sanitizeIssues, serializeStyles, styleGuideToMarkdown, truncateHTML, validateStyleGuide, validateViolations } from './index.js';
3
+ export { default as pc } from 'picocolors';
4
+
5
+ /**
6
+ * Shared logger for UILint packages
7
+ * Outputs styled messages to stderr to avoid interfering with stdout
8
+ */
9
+
10
+ /**
11
+ * Log an info message to stderr
12
+ */
13
+ declare function logInfo(message: string): void;
14
+ /**
15
+ * Log a success message to stderr
16
+ */
17
+ declare function logSuccess(message: string): void;
18
+ /**
19
+ * Log a warning message to stderr
20
+ */
21
+ declare function logWarning(message: string): void;
22
+ /**
23
+ * Log an error message to stderr
24
+ */
25
+ declare function logError(message: string): void;
26
+ /**
27
+ * Log a debug message to stderr (dimmed)
28
+ */
29
+ declare function logDebug(message: string): void;
30
+ /**
31
+ * Create a progress logger that updates the same line
32
+ * Returns methods to update and finish the progress
33
+ */
34
+ declare function createProgress(initialMessage: string): {
35
+ update: (message: string) => void;
36
+ succeed: (message: string) => void;
37
+ fail: (message: string) => void;
38
+ };
3
39
 
4
40
  /**
5
41
  * Ollama bootstrapping utilities (Node.js only).
@@ -119,4 +155,4 @@ declare function findWorkspaceRoot(startDir: string): string;
119
155
  declare function findTailwindConfigPath(startDir: string): string | null;
120
156
  declare function readTailwindThemeTokens(projectDir: string): TailwindThemeTokens | null;
121
157
 
122
- export { DOMSnapshot, STYLEGUIDE_PATHS, TailwindThemeTokens, ensureOllamaInstalledOrExplain, ensureOllamaModelPulled, ensureOllamaReady, ensureOllamaRunning, findStyleGuidePath, findTailwindConfigPath, findUILintStyleGuideUpwards, findWorkspaceRoot, getDefaultStyleGuidePath, hasStdin, isJSON, isOllamaInstalled, parseCLIInput, parseHTML, readStdin, readStyleGuide, readStyleGuideFromProject, readTailwindThemeTokens, styleGuideExists, writeStyleGuide };
158
+ export { DOMSnapshot, STYLEGUIDE_PATHS, TailwindThemeTokens, createProgress, ensureOllamaInstalledOrExplain, ensureOllamaModelPulled, ensureOllamaReady, ensureOllamaRunning, findStyleGuidePath, findTailwindConfigPath, findUILintStyleGuideUpwards, findWorkspaceRoot, getDefaultStyleGuidePath, hasStdin, isJSON, isOllamaInstalled, logDebug, logError, logInfo, logSuccess, logWarning, parseCLIInput, parseHTML, readStdin, readStyleGuide, readStyleGuideFromProject, readTailwindThemeTokens, styleGuideExists, writeStyleGuide };
package/dist/node.js CHANGED
@@ -37,6 +37,56 @@ import {
37
37
  validateViolations
38
38
  } from "./chunk-FIESH4VM.js";
39
39
 
40
+ // src/logger.ts
41
+ import pc from "picocolors";
42
+ var PREFIX = pc.cyan("[uilint]");
43
+ function logInfo(message) {
44
+ console.error(`${PREFIX} ${pc.blue("\u2139")} ${message}`);
45
+ }
46
+ function logSuccess(message) {
47
+ console.error(`${PREFIX} ${pc.green("\u2713")} ${message}`);
48
+ }
49
+ function logWarning(message) {
50
+ console.error(`${PREFIX} ${pc.yellow("\u26A0")} ${message}`);
51
+ }
52
+ function logError(message) {
53
+ console.error(`${PREFIX} ${pc.red("\u2717")} ${message}`);
54
+ }
55
+ function logDebug(message) {
56
+ console.error(`${PREFIX} ${pc.dim(message)}`);
57
+ }
58
+ function createProgress(initialMessage) {
59
+ let lastLength = 0;
60
+ const write = (message) => {
61
+ if (lastLength > 0) {
62
+ process.stderr.write("\r" + " ".repeat(lastLength) + "\r");
63
+ }
64
+ const line = `${PREFIX} ${pc.magenta("\u27F3")} ${message}`;
65
+ process.stderr.write(line);
66
+ lastLength = line.length;
67
+ };
68
+ write(initialMessage);
69
+ return {
70
+ update: (message) => {
71
+ write(message);
72
+ },
73
+ succeed: (message) => {
74
+ if (lastLength > 0) {
75
+ process.stderr.write("\r" + " ".repeat(lastLength) + "\r");
76
+ }
77
+ console.error(`${PREFIX} ${pc.green("\u2713")} ${message}`);
78
+ lastLength = 0;
79
+ },
80
+ fail: (message) => {
81
+ if (lastLength > 0) {
82
+ process.stderr.write("\r" + " ".repeat(lastLength) + "\r");
83
+ }
84
+ console.error(`${PREFIX} ${pc.red("\u2717")} ${message}`);
85
+ lastLength = 0;
86
+ }
87
+ };
88
+ }
89
+
40
90
  // src/ollama/bootstrap.ts
41
91
  import { spawn, spawnSync } from "child_process";
42
92
  import readline from "readline";
@@ -445,6 +495,7 @@ export {
445
495
  createColorRule,
446
496
  createComponentRule,
447
497
  createEmptyStyleGuide,
498
+ createProgress,
448
499
  createSpacingRule,
449
500
  createStyleSummary,
450
501
  createTypographyRule,
@@ -470,6 +521,11 @@ export {
470
521
  hasStdin,
471
522
  isJSON,
472
523
  isOllamaInstalled,
524
+ logDebug,
525
+ logError,
526
+ logInfo,
527
+ logSuccess,
528
+ logWarning,
473
529
  mergeStyleGuides,
474
530
  parseCLIInput,
475
531
  parseGroupedSnapshot,
@@ -477,6 +533,7 @@ export {
477
533
  parseStyleGuide,
478
534
  parseStyleGuideSections,
479
535
  parseViolationsResponse,
536
+ pc,
480
537
  readStdin,
481
538
  readStyleGuide,
482
539
  readStyleGuideFromProject,
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 { ExtractedStyles, 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 any;\n const config = (loaded?.default ?? loaded) as any;\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: any) => any)\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 =\n config.theme && typeof config.theme === \"object\" ? config.theme : {};\n const extend =\n theme.extend && typeof theme.extend === \"object\" ? theme.extend : {};\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/logger.ts","../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 * Shared logger for UILint packages\n * Outputs styled messages to stderr to avoid interfering with stdout\n */\n\nimport pc from \"picocolors\";\n\nconst PREFIX = pc.cyan(\"[uilint]\");\n\n/**\n * Log an info message to stderr\n */\nexport function logInfo(message: string): void {\n console.error(`${PREFIX} ${pc.blue(\"ℹ\")} ${message}`);\n}\n\n/**\n * Log a success message to stderr\n */\nexport function logSuccess(message: string): void {\n console.error(`${PREFIX} ${pc.green(\"✓\")} ${message}`);\n}\n\n/**\n * Log a warning message to stderr\n */\nexport function logWarning(message: string): void {\n console.error(`${PREFIX} ${pc.yellow(\"⚠\")} ${message}`);\n}\n\n/**\n * Log an error message to stderr\n */\nexport function logError(message: string): void {\n console.error(`${PREFIX} ${pc.red(\"✗\")} ${message}`);\n}\n\n/**\n * Log a debug message to stderr (dimmed)\n */\nexport function logDebug(message: string): void {\n console.error(`${PREFIX} ${pc.dim(message)}`);\n}\n\n/**\n * Create a progress logger that updates the same line\n * Returns methods to update and finish the progress\n */\nexport function createProgress(initialMessage: string) {\n let lastLength = 0;\n\n const write = (message: string) => {\n // Clear the previous line if needed\n if (lastLength > 0) {\n process.stderr.write(\"\\r\" + \" \".repeat(lastLength) + \"\\r\");\n }\n const line = `${PREFIX} ${pc.magenta(\"⟳\")} ${message}`;\n process.stderr.write(line);\n lastLength = line.length;\n };\n\n write(initialMessage);\n\n return {\n update: (message: string) => {\n write(message);\n },\n succeed: (message: string) => {\n if (lastLength > 0) {\n process.stderr.write(\"\\r\" + \" \".repeat(lastLength) + \"\\r\");\n }\n console.error(`${PREFIX} ${pc.green(\"✓\")} ${message}`);\n lastLength = 0;\n },\n fail: (message: string) => {\n if (lastLength > 0) {\n process.stderr.write(\"\\r\" + \" \".repeat(lastLength) + \"\\r\");\n }\n console.error(`${PREFIX} ${pc.red(\"✗\")} ${message}`);\n lastLength = 0;\n },\n };\n}\n\n// Re-export picocolors for consistent styling\nexport { pc };\n","/**\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 { ExtractedStyles, 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 any;\n const config = (loaded?.default ?? loaded) as any;\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: any) => any)\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 =\n config.theme && typeof config.theme === \"object\" ? config.theme : {};\n const extend =\n theme.extend && typeof theme.extend === \"object\" ? theme.extend : {};\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uilint-core",
3
- "version": "0.1.32",
3
+ "version": "0.1.34",
4
4
  "description": "Core library for UILint - AI-powered UI consistency checking",
5
5
  "author": "Peter Suggate",
6
6
  "repository": {
@@ -46,6 +46,7 @@
46
46
  "dependencies": {
47
47
  "jsdom": "^27.4.0",
48
48
  "jiti": "^2.5.0",
49
+ "picocolors": "^1.1.1",
49
50
  "tailwindcss": "^4.1.11"
50
51
  },
51
52
  "devDependencies": {