uilint 0.2.67 → 0.2.69
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/{chunk-SQFSFBUP.js → chunk-HASWEVZP.js} +2 -2
- package/dist/{chunk-Y7ZNZFVZ.js → chunk-TKJ27W62.js} +1 -1
- package/dist/chunk-TKJ27W62.js.map +1 -0
- package/dist/{chunk-MSOAR5VP.js → chunk-WRXJULXA.js} +1 -1
- package/dist/chunk-WRXJULXA.js.map +1 -0
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/{init-ui-Z54KCM7G.js → init-ui-ZDR65J2L.js} +4 -4
- package/dist/{plan-ZWGKTWQ3.js → plan-P2XKOXH5.js} +2 -2
- package/dist/{remove-ui-U2VUMSP7.js → remove-ui-US2KVOKZ.js} +3 -3
- package/dist/{upgrade-TGYLZ4QX.js → upgrade-GUYNMGSY.js} +2 -2
- package/package.json +5 -5
- package/dist/chunk-MSOAR5VP.js.map +0 -1
- package/dist/chunk-Y7ZNZFVZ.js.map +0 -1
- /package/dist/{chunk-SQFSFBUP.js.map → chunk-HASWEVZP.js.map} +0 -0
- /package/dist/{init-ui-Z54KCM7G.js.map → init-ui-ZDR65J2L.js.map} +0 -0
- /package/dist/{plan-ZWGKTWQ3.js.map → plan-P2XKOXH5.js.map} +0 -0
- /package/dist/{remove-ui-U2VUMSP7.js.map → remove-ui-US2KVOKZ.js.map} +0 -0
- /package/dist/{upgrade-TGYLZ4QX.js.map → upgrade-GUYNMGSY.js.map} +0 -0
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
execute,
|
|
6
6
|
getAllInstallers,
|
|
7
7
|
getInjectionPoints
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-HASWEVZP.js";
|
|
9
9
|
import {
|
|
10
10
|
detectCoverageSetup
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-WRXJULXA.js";
|
|
12
12
|
import "./chunk-VSBVUS56.js";
|
|
13
13
|
import "./chunk-ZDSDZNIB.js";
|
|
14
14
|
import {
|
|
@@ -1636,7 +1636,7 @@ async function initUI(options = {}, executeOptions = {}) {
|
|
|
1636
1636
|
console.log("\nNo changes selected");
|
|
1637
1637
|
process.exit(0);
|
|
1638
1638
|
}
|
|
1639
|
-
const { createPlan } = await import("./plan-
|
|
1639
|
+
const { createPlan } = await import("./plan-P2XKOXH5.js");
|
|
1640
1640
|
const plan = createPlan(project, choices, { force: options.force });
|
|
1641
1641
|
if (hasRemovals && removeSelections) {
|
|
1642
1642
|
for (const selection of removeSelections) {
|
|
@@ -1688,4 +1688,4 @@ ${pc.blue("Running tests with coverage...")}`);
|
|
|
1688
1688
|
export {
|
|
1689
1689
|
initUI
|
|
1690
1690
|
};
|
|
1691
|
-
//# sourceMappingURL=init-ui-
|
|
1691
|
+
//# sourceMappingURL=init-ui-ZDR65J2L.js.map
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "./chunk-VSBVUS56.js";
|
|
7
7
|
import {
|
|
8
8
|
loadSelectedRules
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-TKJ27W62.js";
|
|
10
10
|
import {
|
|
11
11
|
detectPackageManager
|
|
12
12
|
} from "./chunk-JPE27ROY.js";
|
|
@@ -256,4 +256,4 @@ export {
|
|
|
256
256
|
createPlan,
|
|
257
257
|
getMissingRules
|
|
258
258
|
};
|
|
259
|
-
//# sourceMappingURL=plan-
|
|
259
|
+
//# sourceMappingURL=plan-P2XKOXH5.js.map
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
analyze,
|
|
5
5
|
execute,
|
|
6
6
|
getAllInstallers
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-HASWEVZP.js";
|
|
8
|
+
import "./chunk-WRXJULXA.js";
|
|
9
9
|
import "./chunk-VSBVUS56.js";
|
|
10
10
|
import "./chunk-ZDSDZNIB.js";
|
|
11
11
|
import {
|
|
@@ -598,4 +598,4 @@ async function removeUI(options = {}) {
|
|
|
598
598
|
export {
|
|
599
599
|
removeUI
|
|
600
600
|
};
|
|
601
|
-
//# sourceMappingURL=remove-ui-
|
|
601
|
+
//# sourceMappingURL=remove-ui-US2KVOKZ.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadSelectedRules
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TKJ27W62.js";
|
|
5
5
|
import {
|
|
6
6
|
getInstalledRuleVersions,
|
|
7
7
|
updateManifestRule
|
|
@@ -584,4 +584,4 @@ async function upgrade(options) {
|
|
|
584
584
|
export {
|
|
585
585
|
upgrade
|
|
586
586
|
};
|
|
587
|
-
//# sourceMappingURL=upgrade-
|
|
587
|
+
//# sourceMappingURL=upgrade-GUYNMGSY.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uilint",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.69",
|
|
4
4
|
"description": "CLI for UILint - AI-powered UI consistency checking",
|
|
5
5
|
"author": "Peter Suggate",
|
|
6
6
|
"repository": {
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
"react": "^19.2.3",
|
|
49
49
|
"typescript": "^5.9.3",
|
|
50
50
|
"ws": "^8.19.0",
|
|
51
|
-
"uilint-
|
|
52
|
-
"uilint-
|
|
53
|
-
"uilint-
|
|
51
|
+
"uilint-core": "0.2.69",
|
|
52
|
+
"uilint-duplicates": "0.2.69",
|
|
53
|
+
"uilint-eslint": "0.2.69"
|
|
54
54
|
},
|
|
55
55
|
"optionalDependencies": {
|
|
56
56
|
"@langfuse/client": "^4.5.1",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"ink-testing-library": "^4.0.0",
|
|
67
67
|
"tsup": "^8.5.1",
|
|
68
68
|
"vitest": "^4.0.17",
|
|
69
|
-
"uilint-react": "0.2.
|
|
69
|
+
"uilint-react": "0.2.69"
|
|
70
70
|
},
|
|
71
71
|
"keywords": [
|
|
72
72
|
"cli",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/coverage-detect.ts","../src/utils/next-detect.ts","../src/utils/eslint-config-inject.ts","../src/utils/coverage-prepare.ts"],"sourcesContent":["/**\n * Coverage detection utilities for vitest projects.\n *\n * Detects vitest installation, configuration, and coverage data.\n */\n\nimport { existsSync, readFileSync, statSync } from \"fs\";\nimport { join } from \"path\";\n\nexport interface CoverageSetupInfo {\n /** Whether vitest is in dependencies */\n hasVitest: boolean;\n /** Whether vitest config exists */\n hasVitestConfig: boolean;\n /** Path to vitest config if found */\n vitestConfigPath: string | null;\n /** Whether coverage is configured in vitest */\n hasCoverageConfig: boolean;\n /** Coverage provider (v8 or istanbul) */\n coverageProvider: \"v8\" | \"istanbul\" | null;\n /** Whether coverage data file exists */\n hasCoverageData: boolean;\n /** Path to coverage data */\n coverageDataPath: string | null;\n\n // Preparation flags\n /** Whether @vitest/coverage-v8 (or coverage-istanbul) package is missing */\n needsCoveragePackage: boolean;\n /** Whether vitest.config needs coverage block added */\n needsCoverageConfig: boolean;\n /** Age of coverage data in milliseconds (null if no data) */\n coverageDataAge: number | null;\n}\n\nconst VITEST_CONFIG_FILES = [\n \"vitest.config.ts\",\n \"vitest.config.js\",\n \"vitest.config.mts\",\n \"vitest.config.mjs\",\n];\n\ninterface PackageDepsInfo {\n hasVitest: boolean;\n hasCoveragePackage: boolean;\n}\n\n/**\n * Check if vitest and coverage packages are in package.json\n */\nfunction checkPackageDeps(projectPath: string): PackageDepsInfo {\n try {\n const pkgPath = join(projectPath, \"package.json\");\n if (!existsSync(pkgPath)) return { hasVitest: false, hasCoveragePackage: false };\n\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n\n const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n const hasVitest = \"vitest\" in deps;\n const hasCoveragePackage =\n \"@vitest/coverage-v8\" in deps || \"@vitest/coverage-istanbul\" in deps;\n\n return { hasVitest, hasCoveragePackage };\n } catch {\n return { hasVitest: false, hasCoveragePackage: false };\n }\n}\n\n/**\n * Find the vitest config file in the project\n */\nfunction findVitestConfig(projectPath: string): string | null {\n for (const configFile of VITEST_CONFIG_FILES) {\n const configPath = join(projectPath, configFile);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n return null;\n}\n\n/**\n * Parse vitest config to extract coverage settings\n */\nfunction parseCoverageConfig(configPath: string): {\n hasCoverageConfig: boolean;\n coverageProvider: \"v8\" | \"istanbul\" | null;\n} {\n try {\n const content = readFileSync(configPath, \"utf-8\");\n\n // Check if coverage block exists using regex\n const hasCoverageConfig = /coverage\\s*:\\s*\\{/.test(content);\n\n if (!hasCoverageConfig) {\n return { hasCoverageConfig: false, coverageProvider: null };\n }\n\n // Extract provider value using regex\n // Match patterns like: provider: \"v8\", provider: 'v8', provider: \"istanbul\", etc.\n const providerMatch = content.match(/provider\\s*:\\s*[\"']?(v8|istanbul)[\"']?/);\n const coverageProvider = providerMatch\n ? (providerMatch[1] as \"v8\" | \"istanbul\")\n : null;\n\n return { hasCoverageConfig, coverageProvider };\n } catch {\n return { hasCoverageConfig: false, coverageProvider: null };\n }\n}\n\n/**\n * Check if coverage data file exists and get its age\n */\nfunction findCoverageData(projectPath: string): {\n path: string | null;\n age: number | null;\n} {\n const coverageDataPath = join(projectPath, \"coverage\", \"coverage-final.json\");\n if (existsSync(coverageDataPath)) {\n try {\n const stats = statSync(coverageDataPath);\n const age = Date.now() - stats.mtimeMs;\n return { path: coverageDataPath, age };\n } catch {\n return { path: coverageDataPath, age: null };\n }\n }\n return { path: null, age: null };\n}\n\n/**\n * Detect coverage setup in a project\n */\nexport function detectCoverageSetup(projectPath: string): CoverageSetupInfo {\n const { hasVitest, hasCoveragePackage } = checkPackageDeps(projectPath);\n const vitestConfigPath = findVitestConfig(projectPath);\n const hasVitestConfig = vitestConfigPath !== null;\n\n let hasCoverageConfig = false;\n let coverageProvider: \"v8\" | \"istanbul\" | null = null;\n\n if (vitestConfigPath) {\n const coverageInfo = parseCoverageConfig(vitestConfigPath);\n hasCoverageConfig = coverageInfo.hasCoverageConfig;\n coverageProvider = coverageInfo.coverageProvider;\n }\n\n const coverageData = findCoverageData(projectPath);\n const hasCoverageData = coverageData.path !== null;\n\n // Compute preparation flags\n const needsCoveragePackage = hasVitest && !hasCoveragePackage;\n const needsCoverageConfig = hasVitest && hasVitestConfig && !hasCoverageConfig;\n\n return {\n hasVitest,\n hasVitestConfig,\n vitestConfigPath,\n hasCoverageConfig,\n coverageProvider,\n hasCoverageData,\n coverageDataPath: coverageData.path,\n needsCoveragePackage,\n needsCoverageConfig,\n coverageDataAge: coverageData.age,\n };\n}\n","import { existsSync, readdirSync } from \"fs\";\nimport { join } from \"path\";\n\nexport interface NextAppRouterDetection {\n /**\n * Relative path to the Next App Router root dir (either \"app\" or \"src/app\").\n */\n appRoot: string;\n /**\n * Absolute path to the App Router root dir.\n */\n appRootAbs: string;\n /**\n * Candidate entry files (relative paths) that are good injection targets.\n */\n candidates: string[];\n}\n\nfunction fileExists(projectPath: string, relPath: string): boolean {\n return existsSync(join(projectPath, relPath));\n}\n\nexport function detectNextAppRouter(\n projectPath: string\n): NextAppRouterDetection | null {\n const roots = [\"app\", join(\"src\", \"app\")];\n const candidates: string[] = [];\n\n let chosenRoot: string | null = null;\n for (const root of roots) {\n if (existsSync(join(projectPath, root))) {\n chosenRoot = root;\n break;\n }\n }\n\n if (!chosenRoot) return null;\n\n // Prioritize layout files (Next App Router canonical integration point).\n const entryCandidates = [\n join(chosenRoot, \"layout.tsx\"),\n join(chosenRoot, \"layout.jsx\"),\n join(chosenRoot, \"layout.ts\"),\n join(chosenRoot, \"layout.js\"),\n // Fallbacks (less ideal, but can work):\n join(chosenRoot, \"page.tsx\"),\n join(chosenRoot, \"page.jsx\"),\n ];\n\n for (const rel of entryCandidates) {\n if (fileExists(projectPath, rel)) candidates.push(rel);\n }\n\n // If nothing exists, still return detection so routes can be installed.\n return {\n appRoot: chosenRoot,\n appRootAbs: join(projectPath, chosenRoot),\n candidates,\n };\n}\n\nexport interface NextAppRouterProjectMatch {\n /**\n * Absolute path to the Next project root (dir containing app/ or src/app/).\n */\n projectPath: string;\n detection: NextAppRouterDetection;\n}\n\nconst DEFAULT_IGNORE_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \".next\",\n \"dist\",\n \"build\",\n \"out\",\n \".turbo\",\n \".vercel\",\n \".cursor\",\n \"coverage\",\n \".uilint\",\n]);\n\n/**\n * Best-effort monorepo discovery for Next.js App Router apps.\n *\n * Walks down from `rootDir` looking for directories that contain `app/` or\n * `src/app/`. Skips common large/irrelevant dirs.\n */\nexport function findNextAppRouterProjects(\n rootDir: string,\n options?: { maxDepth?: number; ignoreDirs?: Set<string> }\n): NextAppRouterProjectMatch[] {\n const maxDepth = options?.maxDepth ?? 4;\n const ignoreDirs = options?.ignoreDirs ?? DEFAULT_IGNORE_DIRS;\n const results: NextAppRouterProjectMatch[] = [];\n const visited = new Set<string>();\n\n function walk(dir: string, depth: number) {\n if (depth > maxDepth) return;\n if (visited.has(dir)) return;\n visited.add(dir);\n\n const detection = detectNextAppRouter(dir);\n if (detection) {\n results.push({ projectPath: dir, detection });\n // Don't descend further once we found a project root (avoid nested hits).\n return;\n }\n\n let entries: Array<{ name: string; isDirectory: boolean }> = [];\n try {\n entries = readdirSync(dir, { withFileTypes: true }).map((d) => ({\n name: d.name,\n isDirectory: d.isDirectory(),\n }));\n } catch {\n return;\n }\n\n for (const ent of entries) {\n if (!ent.isDirectory) continue;\n if (ignoreDirs.has(ent.name)) continue;\n // Skip hidden dirs by default (except `src` which matters)\n if (ent.name.startsWith(\".\") && ent.name !== \".\") continue;\n walk(join(dir, ent.name), depth + 1);\n }\n }\n\n walk(rootDir, 0);\n return results;\n}\n","/**\n * Inject uilint-eslint rules into ESLint config\n *\n * Modifies eslint.config.{ts,mjs,js,cjs} to add uilint import and selected rules\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"fs\";\nimport { join, relative, dirname } from \"path\";\nimport type { RuleMetadata } from \"uilint-eslint\";\nimport { parseExpression, parseModule, generateCode } from \"magicast\";\nimport { findWorkspaceRoot } from \"uilint-core/node\";\n\nexport interface InstallEslintPluginOptions {\n projectPath: string;\n selectedRules: RuleMetadata[];\n force?: boolean;\n confirmAddMissingRules?: (\n relPath: string,\n missingRules: RuleMetadata[]\n ) => Promise<boolean>;\n}\n\nconst CONFIG_EXTENSIONS = [\".ts\", \".mjs\", \".js\", \".cjs\"];\n\n/**\n * Find the eslint.config file in a project\n */\nexport function findEslintConfigFile(projectPath: string): string | null {\n for (const ext of CONFIG_EXTENSIONS) {\n const configPath = join(projectPath, `eslint.config${ext}`);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n return null;\n}\n\n/**\n * Get the relative config filename for display\n */\nexport function getEslintConfigFilename(configPath: string): string {\n const parts = configPath.split(\"/\");\n return parts[parts.length - 1] || \"eslint.config.mjs\";\n}\n\n/**\n * Check if the source already has uilint rules configured\n */\nfunction hasUilintRules(source: string): boolean {\n return source.includes('\"uilint/') || source.includes(\"'uilint/\");\n}\n\n/**\n * Check if the source already has uilint imported\n */\nfunction hasUilintImport(source: string): boolean {\n return (\n source.includes('from \"uilint-eslint\"') ||\n source.includes(\"from 'uilint-eslint'\") ||\n source.includes('require(\"uilint-eslint\")') ||\n source.includes(\"require('uilint-eslint')\")\n );\n}\n\ntype UilintEslintConfigInfo = {\n /** Set of configured `uilint/*` rule IDs (without the `uilint/` prefix). */\n configuredRuleIds: Set<string>;\n /** Whether config appears to configure uilint rules. */\n configured: boolean;\n};\n\nfunction walkAst(node: any, visit: (n: any) => void): void {\n if (!node || typeof node !== \"object\") return;\n visit(node);\n for (const key of Object.keys(node)) {\n const v = (node as any)[key];\n if (!v) continue;\n if (Array.isArray(v)) {\n for (const item of v) walkAst(item, visit);\n } else if (typeof v === \"object\" && v.type) {\n walkAst(v, visit);\n }\n }\n}\n\nfunction isIdentifier(node: any, name?: string): boolean {\n return (\n !!node &&\n node.type === \"Identifier\" &&\n (name ? node.name === name : typeof node.name === \"string\")\n );\n}\n\nfunction isStringLiteral(node: any): node is { type: string; value: string } {\n return (\n !!node &&\n (node.type === \"StringLiteral\" || node.type === \"Literal\") &&\n typeof node.value === \"string\"\n );\n}\n\nfunction getObjectPropertyValue(obj: any, keyName: string): any | null {\n if (!obj || obj.type !== \"ObjectExpression\") return null;\n for (const prop of obj.properties ?? []) {\n if (!prop) continue;\n if (prop.type === \"ObjectProperty\" || prop.type === \"Property\") {\n const key = prop.key;\n const keyMatch =\n (key?.type === \"Identifier\" && key.name === keyName) ||\n (isStringLiteral(key) && key.value === keyName);\n if (keyMatch) return prop.value;\n }\n }\n return null;\n}\n\nfunction hasSpreadProperties(obj: any): boolean {\n if (!obj || obj.type !== \"ObjectExpression\") return false;\n return (obj.properties ?? []).some(\n (p: any) => p && (p.type === \"SpreadElement\" || p.type === \"SpreadProperty\")\n );\n}\n\nconst IGNORED_AST_KEYS = new Set([\n \"loc\",\n \"start\",\n \"end\",\n \"extra\",\n \"leadingComments\",\n \"trailingComments\",\n \"innerComments\",\n]);\n\nfunction normalizeAstForCompare(node: any): any {\n if (node === null) return null;\n if (node === undefined) return undefined;\n if (typeof node !== \"object\") return node;\n if (Array.isArray(node)) return node.map(normalizeAstForCompare);\n\n const out: Record<string, any> = {};\n const keys = Object.keys(node)\n .filter((k) => !IGNORED_AST_KEYS.has(k))\n .sort();\n for (const k of keys) {\n // Avoid proxy-ish or non-serializable fields if present.\n if (k.startsWith(\"$\")) continue;\n out[k] = normalizeAstForCompare(node[k]);\n }\n return out;\n}\n\nfunction astEquivalent(a: any, b: any): boolean {\n try {\n return (\n JSON.stringify(normalizeAstForCompare(a)) ===\n JSON.stringify(normalizeAstForCompare(b))\n );\n } catch {\n return false;\n }\n}\n\nfunction collectUilintRuleIdsFromRulesObject(rulesObj: any): Set<string> {\n const ids = new Set<string>();\n if (!rulesObj || rulesObj.type !== \"ObjectExpression\") return ids;\n for (const prop of rulesObj.properties ?? []) {\n if (!prop) continue;\n if (prop.type !== \"ObjectProperty\" && prop.type !== \"Property\") continue;\n const key = prop.key;\n if (!isStringLiteral(key)) continue;\n const val = key.value;\n if (typeof val !== \"string\") continue;\n if (val.startsWith(\"uilint/\")) {\n ids.add(val.slice(\"uilint/\".length));\n }\n }\n return ids;\n}\n\nfunction findExportedConfigArrayExpression(mod: any): {\n kind: \"esm\" | \"cjs\";\n arrayExpr: any;\n program: any;\n} | null {\n function unwrapExpression(expr: any): any {\n let e = expr;\n // Best-effort unwrap for TS/parenthesized wrappers. (These can appear if the\n // config is authored in TS/JS with type assertions or parentheses.)\n while (e) {\n if (e.type === \"TSAsExpression\" || e.type === \"TSNonNullExpression\") {\n e = e.expression;\n continue;\n }\n if (e.type === \"TSSatisfiesExpression\") {\n e = e.expression;\n continue;\n }\n if (e.type === \"ParenthesizedExpression\") {\n e = e.expression;\n continue;\n }\n break;\n }\n return e;\n }\n\n function resolveTopLevelIdentifierToArrayExpr(\n program: any,\n name: string\n ): any | null {\n if (!program || program.type !== \"Program\") return null;\n for (const stmt of program.body ?? []) {\n if (stmt?.type !== \"VariableDeclaration\") continue;\n for (const decl of stmt.declarations ?? []) {\n const id = decl?.id;\n if (!isIdentifier(id, name)) continue;\n const init = unwrapExpression(decl?.init);\n if (!init) return null;\n if (init.type === \"ArrayExpression\") return init;\n if (\n init.type === \"CallExpression\" &&\n isIdentifier(init.callee, \"defineConfig\") &&\n unwrapExpression(init.arguments?.[0])?.type === \"ArrayExpression\"\n ) {\n return unwrapExpression(init.arguments?.[0]);\n }\n return null;\n }\n }\n return null;\n }\n\n // Prefer reading directly from the program AST so we can handle:\n // - export default [ ... ]\n // - export default defineConfig([ ... ])\n // - export default eslintConfig; (where eslintConfig is a top-level array)\n const program = mod?.$ast;\n if (program && program.type === \"Program\") {\n for (const stmt of program.body ?? []) {\n if (!stmt || stmt.type !== \"ExportDefaultDeclaration\") continue;\n const decl = unwrapExpression(stmt.declaration);\n if (!decl) break;\n\n if (decl.type === \"ArrayExpression\") {\n return { kind: \"esm\", arrayExpr: decl, program };\n }\n if (\n decl.type === \"CallExpression\" &&\n isIdentifier(decl.callee, \"defineConfig\") &&\n unwrapExpression(decl.arguments?.[0])?.type === \"ArrayExpression\"\n ) {\n return {\n kind: \"esm\",\n arrayExpr: unwrapExpression(decl.arguments?.[0]),\n program,\n };\n }\n if (decl.type === \"Identifier\" && typeof decl.name === \"string\") {\n const resolved = resolveTopLevelIdentifierToArrayExpr(\n program,\n decl.name\n );\n if (resolved) return { kind: \"esm\", arrayExpr: resolved, program };\n }\n break;\n }\n }\n\n // CommonJS: module.exports = [ ... ] OR module.exports = defineConfig([ ... ])\n if (!program || program.type !== \"Program\") return null;\n\n for (const stmt of program.body ?? []) {\n if (!stmt || stmt.type !== \"ExpressionStatement\") continue;\n const expr = stmt.expression;\n if (!expr || expr.type !== \"AssignmentExpression\") continue;\n const left = expr.left;\n const right = expr.right;\n const isModuleExports =\n left?.type === \"MemberExpression\" &&\n isIdentifier(left.object, \"module\") &&\n isIdentifier(left.property, \"exports\");\n if (!isModuleExports) continue;\n\n if (right?.type === \"ArrayExpression\") {\n return { kind: \"cjs\", arrayExpr: right, program };\n }\n if (\n right?.type === \"CallExpression\" &&\n isIdentifier(right.callee, \"defineConfig\") &&\n right.arguments?.[0]?.type === \"ArrayExpression\"\n ) {\n return { kind: \"cjs\", arrayExpr: right.arguments[0], program };\n }\n if (right?.type === \"Identifier\" && typeof right.name === \"string\") {\n const resolved = resolveTopLevelIdentifierToArrayExpr(\n program,\n right.name\n );\n if (resolved) return { kind: \"cjs\", arrayExpr: resolved, program };\n }\n }\n\n return null;\n}\n\nfunction collectConfiguredUilintRuleIdsFromConfigArray(\n arrayExpr: any\n): Set<string> {\n const ids = new Set<string>();\n if (!arrayExpr || arrayExpr.type !== \"ArrayExpression\") return ids;\n for (const el of arrayExpr.elements ?? []) {\n if (!el || el.type !== \"ObjectExpression\") continue;\n const rules = getObjectPropertyValue(el, \"rules\");\n for (const id of collectUilintRuleIdsFromRulesObject(rules)) ids.add(id);\n }\n return ids;\n}\n\nfunction findExistingUilintRulesObject(arrayExpr: any): {\n configObj: any | null;\n rulesObj: any | null;\n safeToMutate: boolean;\n} {\n if (!arrayExpr || arrayExpr.type !== \"ArrayExpression\") {\n return { configObj: null, rulesObj: null, safeToMutate: false };\n }\n\n for (const el of arrayExpr.elements ?? []) {\n if (!el || el.type !== \"ObjectExpression\") continue;\n\n const plugins = getObjectPropertyValue(el, \"plugins\");\n const rules = getObjectPropertyValue(el, \"rules\");\n\n const hasUilintPlugin =\n plugins?.type === \"ObjectExpression\" &&\n getObjectPropertyValue(plugins, \"uilint\") !== null;\n\n const uilintIds = collectUilintRuleIdsFromRulesObject(rules);\n const hasUilintRules = uilintIds.size > 0;\n\n if (!hasUilintPlugin && !hasUilintRules) continue;\n\n const safe =\n rules?.type === \"ObjectExpression\" && !hasSpreadProperties(rules);\n return { configObj: el, rulesObj: rules, safeToMutate: safe };\n }\n\n return { configObj: null, rulesObj: null, safeToMutate: false };\n}\n\nfunction collectTopLevelBindings(program: any): Set<string> {\n const names = new Set<string>();\n if (!program || program.type !== \"Program\") return names;\n\n for (const stmt of program.body ?? []) {\n if (stmt?.type === \"ImportDeclaration\") {\n for (const spec of stmt.specifiers ?? []) {\n const local = spec?.local;\n if (local?.type === \"Identifier\" && typeof local.name === \"string\") {\n names.add(local.name);\n }\n }\n continue;\n }\n if (stmt?.type === \"VariableDeclaration\") {\n for (const decl of stmt.declarations ?? []) {\n const id = decl?.id;\n if (id?.type === \"Identifier\" && typeof id.name === \"string\") {\n names.add(id.name);\n }\n }\n } else if (stmt?.type === \"FunctionDeclaration\") {\n if (stmt.id?.type === \"Identifier\" && typeof stmt.id.name === \"string\") {\n names.add(stmt.id.name);\n }\n }\n }\n return names;\n}\n\nfunction chooseUniqueIdentifier(base: string, used: Set<string>): string {\n if (!used.has(base)) return base;\n let i = 2;\n while (used.has(`${base}${i}`)) i++;\n return `${base}${i}`;\n}\n\nfunction findExistingDefaultImportLocalName(\n program: any,\n from: string\n): string | null {\n if (!program || program.type !== \"Program\") return null;\n for (const stmt of program.body ?? []) {\n if (stmt?.type !== \"ImportDeclaration\") continue;\n const src = stmt.source?.value;\n if (typeof src !== \"string\" || src !== from) continue;\n for (const spec of stmt.specifiers ?? []) {\n if (spec?.type === \"ImportDefaultSpecifier\") {\n const local = spec.local;\n if (local?.type === \"Identifier\" && typeof local.name === \"string\") {\n return local.name;\n }\n }\n }\n }\n return null;\n}\n\n/**\n * Add imports for local rules from .uilint/rules/\n */\nfunction addLocalRuleImportsAst(\n mod: any,\n selectedRules: RuleMetadata[],\n configPath: string,\n rulesRoot: string,\n fileExtension: string = \".js\",\n isTypeScriptProject: boolean = false\n): { importNames: Map<string, string>; changed: boolean } {\n const importNames = new Map<string, string>();\n let changed = false;\n\n // Calculate relative path from config file to .uilint/rules/\n const configDir = dirname(configPath);\n const rulesDir = join(rulesRoot, \".uilint\", \"rules\");\n const relativeRulesPath = relative(configDir, rulesDir).replace(/\\\\/g, \"/\");\n\n // Ensure it starts with ./ or ../ (note: `.foo` is NOT a valid relative import)\n const normalizedRulesPath =\n relativeRulesPath.startsWith(\"./\") || relativeRulesPath.startsWith(\"../\")\n ? relativeRulesPath\n : `./${relativeRulesPath}`;\n\n const used = collectTopLevelBindings(mod.$ast);\n\n for (const rule of selectedRules) {\n // Directory-based rules use /index for TypeScript projects only\n // For JavaScript projects, all rules are bundled to single .js files\n const rulePath = (rule.isDirectoryBased && isTypeScriptProject)\n ? `${normalizedRulesPath}/${rule.id}/index${fileExtension}`\n : `${normalizedRulesPath}/${rule.id}${fileExtension}`;\n\n // If the import already exists (possibly with a different local name),\n // reuse it. Magicast can't always rename existing imports.\n const existingLocal = findExistingDefaultImportLocalName(mod.$ast, rulePath);\n if (existingLocal) {\n importNames.set(rule.id, existingLocal);\n used.add(existingLocal);\n continue;\n }\n\n // Generate a safe import name (e.g., noArbitraryTailwindRule)\n const importName = chooseUniqueIdentifier(\n `${rule.id\n .replace(/-([a-z])/g, (_: string, c: string) => c.toUpperCase())\n .replace(/^./, (c: string) => c.toUpperCase())}Rule`,\n used\n );\n importNames.set(rule.id, importName);\n used.add(importName);\n\n // Add import statement\n mod.imports.$add({\n imported: \"default\",\n local: importName,\n from: rulePath,\n });\n changed = true;\n }\n\n return { importNames, changed };\n}\n\n/**\n * Add require statements for local rules (CommonJS)\n */\nfunction addLocalRuleRequiresAst(\n program: any,\n selectedRules: RuleMetadata[],\n configPath: string,\n rulesRoot: string,\n fileExtension: string = \".js\",\n isTypeScriptProject: boolean = false\n): { importNames: Map<string, string>; changed: boolean } {\n const importNames = new Map<string, string>();\n let changed = false;\n\n if (!program || program.type !== \"Program\") {\n return { importNames, changed };\n }\n\n // Calculate relative path from config file to .uilint/rules/\n const configDir = dirname(configPath);\n const rulesDir = join(rulesRoot, \".uilint\", \"rules\");\n const relativeRulesPath = relative(configDir, rulesDir).replace(/\\\\/g, \"/\");\n\n // Ensure it starts with ./ or ../ (note: `.foo` is NOT a valid relative require)\n const normalizedRulesPath =\n relativeRulesPath.startsWith(\"./\") || relativeRulesPath.startsWith(\"../\")\n ? relativeRulesPath\n : `./${relativeRulesPath}`;\n\n const used = collectTopLevelBindings(program);\n\n for (const rule of selectedRules) {\n // Generate a safe import name\n const importName = chooseUniqueIdentifier(\n `${rule.id\n .replace(/-([a-z])/g, (_: string, c: string) => c.toUpperCase())\n .replace(/^./, (c: string) => c.toUpperCase())}Rule`,\n used\n );\n importNames.set(rule.id, importName);\n used.add(importName);\n\n // Add require statement\n // Directory-based rules use /index for TypeScript projects only\n // For JavaScript projects, all rules are bundled to single .js files\n const rulePath = (rule.isDirectoryBased && isTypeScriptProject)\n ? `${normalizedRulesPath}/${rule.id}/index${fileExtension}`\n : `${normalizedRulesPath}/${rule.id}${fileExtension}`;\n const stmtMod = parseModule(\n `const ${importName} = require(\"${rulePath}\");`\n );\n const stmt = (stmtMod.$ast as any).body?.[0];\n if (stmt) {\n // Place after a leading \"use strict\" if present.\n let insertAt = 0;\n const first = program.body?.[0];\n if (\n first?.type === \"ExpressionStatement\" &&\n first.expression?.type === \"StringLiteral\" &&\n first.expression.value === \"use strict\"\n ) {\n insertAt = 1;\n }\n program.body.splice(insertAt, 0, stmt);\n changed = true;\n }\n }\n\n return { importNames, changed };\n}\n\nfunction appendUilintConfigBlockToArray(\n arrayExpr: any,\n selectedRules: RuleMetadata[],\n ruleImportNames: Map<string, string>\n): void {\n // Build plugin object with local rule imports\n const pluginRulesCode = Array.from(ruleImportNames.entries())\n .map(([ruleId, importName]) => ` \"${ruleId}\": ${importName},`)\n .join(\"\\n\");\n\n const rulesPropsCode = selectedRules\n .map((r) => {\n const ruleKey = `uilint/${r.id}`;\n const valueCode =\n r.defaultOptions && r.defaultOptions.length > 0\n ? `[\"${r.defaultSeverity}\", ...${JSON.stringify(\n r.defaultOptions,\n null,\n 2\n )}]`\n : `\"${r.defaultSeverity}\"`;\n return ` \"${ruleKey}\": ${valueCode},`;\n })\n .join(\"\\n\");\n\n const blockCode = `{\n files: [\n \"src/**/*.{js,jsx,ts,tsx}\",\n \"app/**/*.{js,jsx,ts,tsx}\",\n \"pages/**/*.{js,jsx,ts,tsx}\",\n ],\n plugins: {\n uilint: {\n rules: {\n${pluginRulesCode}\n },\n },\n },\n rules: {\n${rulesPropsCode}\n },\n }`;\n\n const objExpr = (parseExpression(blockCode) as any).$ast;\n arrayExpr.elements.push(objExpr);\n}\n\n/**\n * Update an existing uilint config block with new rules.\n * This modifies the existing config object instead of creating a new one.\n *\n * Handles both plugin formats:\n * - `plugins: { uilint: uilint }` (old format with identifier)\n * - `plugins: { uilint: { rules: {...} } }` (new format with rules object)\n */\nfunction updateExistingUilintConfigBlock(\n configObj: any,\n selectedRules: RuleMetadata[],\n ruleImportNames: Map<string, string>\n): void {\n const pluginsObj = getObjectPropertyValue(configObj, \"plugins\");\n const rulesObj = getObjectPropertyValue(configObj, \"rules\");\n\n // Update the plugins.uilint object to include the new rule imports\n if (pluginsObj?.type === \"ObjectExpression\") {\n const uilintPluginProp = pluginsObj.properties?.find(\n (p: any) =>\n (p.type === \"ObjectProperty\" || p.type === \"Property\") &&\n ((p.key?.type === \"Identifier\" && p.key.name === \"uilint\") ||\n (isStringLiteral(p.key) && p.key.value === \"uilint\"))\n );\n\n if (uilintPluginProp) {\n const uilintValue = uilintPluginProp.value;\n\n // Check if it's the old format (identifier like `uilint`) or new format (object with rules)\n if (uilintValue?.type === \"Identifier\") {\n // Old format: `plugins: { uilint: uilint }`\n // Replace with new format: `plugins: { uilint: { rules: {...} } }`\n const pluginRulesCode = Array.from(ruleImportNames.entries())\n .map(([ruleId, importName]) => `\"${ruleId}\": ${importName}`)\n .join(\", \");\n\n const newPluginObjCode = `{ rules: { ${pluginRulesCode} } }`;\n const newPluginObj = (parseExpression(newPluginObjCode) as any).$ast;\n uilintPluginProp.value = newPluginObj;\n } else if (uilintValue?.type === \"ObjectExpression\") {\n // New format: `plugins: { uilint: { rules: {...} } }`\n // Find or create the rules object inside\n let pluginRulesObj = getObjectPropertyValue(uilintValue, \"rules\");\n\n if (!pluginRulesObj || pluginRulesObj.type !== \"ObjectExpression\") {\n // Create the rules property\n const rulesCode = `{ rules: {} }`;\n const tempObj = (parseExpression(rulesCode) as any).$ast;\n pluginRulesObj = tempObj.properties[0].value;\n uilintValue.properties.push(tempObj.properties[0]);\n }\n\n // Add each rule import to the plugins.uilint.rules object\n for (const [ruleId, importName] of ruleImportNames.entries()) {\n // Check if already exists\n const exists = pluginRulesObj.properties?.some(\n (p: any) =>\n (p.type === \"ObjectProperty\" || p.type === \"Property\") &&\n isStringLiteral(p.key) &&\n p.key.value === ruleId\n );\n\n if (!exists) {\n const propCode = `{ \"${ruleId}\": ${importName} }`;\n const tempObj = (parseExpression(propCode) as any).$ast;\n pluginRulesObj.properties.push(tempObj.properties[0]);\n }\n }\n }\n }\n }\n\n // Add new rules or update existing ones in the rules object\n if (rulesObj?.type === \"ObjectExpression\") {\n for (const rule of selectedRules) {\n const ruleKey = `uilint/${rule.id}`;\n\n // Generate the new value for this rule\n const valueCode =\n rule.defaultOptions && rule.defaultOptions.length > 0\n ? `[\"${rule.defaultSeverity}\", ...${JSON.stringify(\n rule.defaultOptions,\n null,\n 2\n )}]`\n : `\"${rule.defaultSeverity}\"`;\n\n // Find existing rule property\n const existingPropIndex = rulesObj.properties?.findIndex(\n (p: any) =>\n (p.type === \"ObjectProperty\" || p.type === \"Property\") &&\n isStringLiteral(p.key) &&\n p.key.value === ruleKey\n );\n\n const propCode = `{ \"${ruleKey}\": ${valueCode} }`;\n const tempObj = (parseExpression(propCode) as any).$ast;\n const newProp = tempObj.properties[0];\n\n if (existingPropIndex !== undefined && existingPropIndex >= 0) {\n // Update existing rule with new value\n rulesObj.properties[existingPropIndex] = newProp;\n } else {\n // Add new rule\n rulesObj.properties.push(newProp);\n }\n }\n }\n}\n\nfunction getUilintEslintConfigInfoFromSourceAst(source: string):\n | {\n info: UilintEslintConfigInfo;\n mod: any;\n arrayExpr: any;\n kind: \"esm\" | \"cjs\";\n existingConfig: {\n configObj: any | null;\n rulesObj: any | null;\n safeToMutate: boolean;\n };\n }\n | { error: string } {\n try {\n const mod = parseModule(source);\n const found = findExportedConfigArrayExpression(mod);\n if (!found) {\n return {\n error:\n \"Could not locate an exported ESLint flat config array (expected `export default [...]`, `export default defineConfig([...])`, `module.exports = [...]`, or `module.exports = defineConfig([...])`).\",\n };\n }\n\n const configuredRuleIds = collectConfiguredUilintRuleIdsFromConfigArray(\n found.arrayExpr\n );\n const existingUilint = findExistingUilintRulesObject(found.arrayExpr);\n const configured =\n configuredRuleIds.size > 0 || existingUilint.configObj !== null;\n\n return {\n info: { configuredRuleIds, configured },\n mod,\n arrayExpr: found.arrayExpr,\n kind: found.kind,\n existingConfig: existingUilint,\n };\n } catch {\n return {\n error:\n \"Unable to parse ESLint config as JavaScript. Please update it manually or simplify the config so it can be safely auto-modified.\",\n };\n }\n}\n\nexport function getUilintEslintConfigInfoFromSource(\n source: string\n): UilintEslintConfigInfo {\n const ast = getUilintEslintConfigInfoFromSourceAst(source);\n if (\"error\" in ast) {\n // Fallback (best-effort) to string heuristics for scan-only scenarios.\n const configuredRuleIds = extractConfiguredUilintRuleIds(source);\n return {\n configuredRuleIds,\n configured: configuredRuleIds.size > 0,\n };\n }\n return ast.info;\n}\n\nfunction findEsmExportedConfigArrayStartIndex(source: string): number | null {\n // Supported:\n // - export default [ ... ]\n // - export default defineConfig([ ... ])\n const patterns: RegExp[] = [\n /export\\s+default\\s+\\[/,\n /export\\s+default\\s+defineConfig\\s*\\(\\s*\\[/,\n ];\n\n for (const re of patterns) {\n const m = source.match(re);\n if (!m || m.index === undefined) continue;\n return m.index + m[0].length;\n }\n\n return null;\n}\n\nfunction findCommonJsExportedConfigArrayStartIndex(\n source: string\n): number | null {\n // Supported:\n // - module.exports = [ ... ]\n // - module.exports = defineConfig([ ... ]) (best-effort)\n const patterns: RegExp[] = [\n /module\\.exports\\s*=\\s*\\[/,\n /module\\.exports\\s*=\\s*defineConfig\\s*\\(\\s*\\[/,\n ];\n\n for (const re of patterns) {\n const m = source.match(re);\n if (!m || m.index === undefined) continue;\n return m.index + m[0].length;\n }\n\n return null;\n}\n\n/**\n * Extract configured uilint rule IDs from source.\n * Matches keys like: \"uilint/no-arbitrary-tailwind\": \"error\"\n */\nfunction extractConfiguredUilintRuleIds(source: string): Set<string> {\n const ids = new Set<string>();\n const re = /[\"']uilint\\/([^\"']+)[\"']\\s*:/g;\n for (const m of source.matchAll(re)) {\n if (m[1]) ids.add(m[1]);\n }\n return ids;\n}\n\nfunction getMissingSelectedRules(\n selectedRules: RuleMetadata[],\n configuredIds: Set<string>\n): RuleMetadata[] {\n return selectedRules.filter((r) => !configuredIds.has(r.id));\n}\n\n/**\n * Get rules that exist but need updating (different options or severity)\n */\nfunction buildDesiredRuleValueExpression(rule: RuleMetadata): string {\n if (rule.defaultOptions && rule.defaultOptions.length > 0) {\n // Match the shape we generate elsewhere: [\"severity\", ...[options...]]\n return `[\"${rule.defaultSeverity}\", ...${JSON.stringify(\n rule.defaultOptions,\n null,\n 2\n )}]`;\n }\n return `\"${rule.defaultSeverity}\"`;\n}\n\nfunction collectUilintRuleValueNodesFromConfigArray(\n arrayExpr: any\n): Map<string, any> {\n const out = new Map<string, any>();\n if (!arrayExpr || arrayExpr.type !== \"ArrayExpression\") return out;\n\n for (const el of arrayExpr.elements ?? []) {\n if (!el || el.type !== \"ObjectExpression\") continue;\n const rules = getObjectPropertyValue(el, \"rules\");\n if (!rules || rules.type !== \"ObjectExpression\") continue;\n\n for (const prop of rules.properties ?? []) {\n if (!prop) continue;\n if (prop.type !== \"ObjectProperty\" && prop.type !== \"Property\") continue;\n const key = prop.key;\n if (!isStringLiteral(key)) continue;\n const k = key.value;\n if (typeof k !== \"string\" || !k.startsWith(\"uilint/\")) continue;\n const id = k.slice(\"uilint/\".length);\n // First occurrence wins; that's enough for detecting up-to-date configs.\n if (!out.has(id)) out.set(id, prop.value);\n }\n }\n\n return out;\n}\n\nfunction getRulesNeedingUpdate(\n selectedRules: RuleMetadata[],\n configuredIds: Set<string>,\n arrayExpr: any\n): RuleMetadata[] {\n // Only consider rules that are already configured, and only update if the\n // existing severity/options differ from what we would generate.\n const existingVals = collectUilintRuleValueNodesFromConfigArray(arrayExpr);\n\n return selectedRules.filter((r) => {\n if (!configuredIds.has(r.id)) return false;\n const existing = existingVals.get(r.id);\n if (!existing) return true;\n\n const desiredExpr = buildDesiredRuleValueExpression(r);\n const desiredAst = (parseExpression(desiredExpr) as any).$ast;\n return !astEquivalent(existing, desiredAst);\n });\n}\n\n/**\n * Generate a single rule config string\n */\nfunction generateSingleRuleConfig(rule: RuleMetadata): string {\n const ruleKey = `\"uilint/${rule.id}\"`;\n\n if (rule.defaultOptions && rule.defaultOptions.length > 0) {\n // Rule with options\n const optionsStr = JSON.stringify(rule.defaultOptions, null, 6)\n .split(\"\\n\")\n .join(\"\\n \");\n return ` ${ruleKey}: [\"${rule.defaultSeverity}\", ...${optionsStr}],`;\n } else {\n // Simple rule\n return ` ${ruleKey}: \"${rule.defaultSeverity}\",`;\n }\n}\n\n/**\n * Add the uilint import to the source if not present\n */\nfunction ensureUilintImport(source: string, isCommonJS: boolean): string {\n if (hasUilintImport(source)) {\n return source;\n }\n\n const importLine = isCommonJS\n ? `const uilint = require(\"uilint-eslint\");\\n`\n : `import uilint from \"uilint-eslint\";\\n`;\n\n // Find the last import/require statement and insert after it\n const header = source.slice(0, Math.min(source.length, 5000));\n const importRegex = isCommonJS\n ? /^(?:const|var|let)\\s+.*?=\\s*require\\([^)]+\\);?\\s*$/gm\n : /^import[\\s\\S]*?;\\s*$/gm;\n\n let lastImportEnd = -1;\n for (const m of header.matchAll(importRegex)) {\n lastImportEnd = (m.index ?? 0) + m[0].length;\n }\n\n if (lastImportEnd !== -1) {\n return (\n source.slice(0, lastImportEnd) +\n \"\\n\" +\n importLine +\n source.slice(lastImportEnd)\n );\n }\n\n // No imports found, add at the beginning\n return importLine + source;\n}\n\n/**\n * Generate the rules config object from selected rules\n */\nfunction generateRulesConfig(selectedRules: RuleMetadata[]): string {\n const lines: string[] = [];\n\n for (const rule of selectedRules) {\n const ruleKey = `\"uilint/${rule.id}\"`;\n\n if (rule.defaultOptions && rule.defaultOptions.length > 0) {\n // Rule with options\n const optionsStr = JSON.stringify(rule.defaultOptions, null, 6)\n .split(\"\\n\")\n .join(\"\\n \");\n lines.push(\n ` ${ruleKey}: [\"${rule.defaultSeverity}\", ...${optionsStr}],`\n );\n } else {\n // Simple rule\n lines.push(` ${ruleKey}: \"${rule.defaultSeverity}\",`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction detectIndent(source: string, index: number): string {\n const lineStart = source.lastIndexOf(\"\\n\", index);\n const start = lineStart === -1 ? 0 : lineStart + 1;\n const line = source.slice(start, index);\n const m = line.match(/^\\s*/);\n return m?.[0] ?? \"\";\n}\n\n/**\n * Insert missing uilint rule keys into an existing `rules: { ... }` object\n * that already contains at least one \"uilint/\" key.\n *\n * This is intentionally a best-effort string transform (no JS AST dependency).\n */\nfunction insertMissingRulesIntoExistingRulesObject(\n source: string,\n missingRules: RuleMetadata[]\n): string {\n if (missingRules.length === 0) return source;\n\n // Anchor on an existing uilint rule key, then look backwards for the\n // nearest `rules:` preceding it.\n const uilintKeyMatch = source.match(/[\"']uilint\\/[^\"']+[\"']\\s*:/);\n if (!uilintKeyMatch || uilintKeyMatch.index === undefined) return source;\n\n const uilintKeyIndex = uilintKeyMatch.index;\n const searchStart = Math.max(0, uilintKeyIndex - 4000);\n const before = source.slice(searchStart, uilintKeyIndex);\n const rulesKwIndexRel = before.lastIndexOf(\"rules\");\n if (rulesKwIndexRel === -1) return source;\n\n const rulesKwIndex = searchStart + rulesKwIndexRel;\n const braceOpenIndex = source.indexOf(\"{\", rulesKwIndex);\n if (braceOpenIndex === -1 || braceOpenIndex > uilintKeyIndex) return source;\n\n // Find the matching closing brace for the rules object.\n let depth = 0;\n let braceCloseIndex = -1;\n for (let i = braceOpenIndex; i < source.length; i++) {\n const ch = source[i];\n if (ch === \"{\") depth++;\n else if (ch === \"}\") {\n depth--;\n if (depth === 0) {\n braceCloseIndex = i;\n break;\n }\n }\n }\n if (braceCloseIndex === -1) return source;\n\n const rulesIndent = detectIndent(source, braceOpenIndex);\n const entryIndent = rulesIndent + \" \";\n const entryTextRaw = generateRulesConfig(missingRules);\n const entryText = entryTextRaw\n .split(\"\\n\")\n .map((l) => (l.trim().length === 0 ? l : entryIndent + l.trimStart()))\n .join(\"\\n\");\n\n const insertion =\n (source.slice(braceOpenIndex + 1, braceCloseIndex).trim().length === 0\n ? \"\\n\"\n : \"\\n\") +\n entryText +\n \"\\n\" +\n rulesIndent;\n\n return (\n source.slice(0, braceCloseIndex) + insertion + source.slice(braceCloseIndex)\n );\n}\n\n/**\n * Find the end of a rule value in the source code\n * Handles: \"error\", [\"error\"], [\"error\", {...}], [\"error\", ...[{...}]]\n */\nfunction findRuleValueEnd(source: string, startIndex: number): number {\n let pos = startIndex;\n let depth = 0;\n let inString = false;\n let stringChar = \"\";\n let foundArray = false;\n\n while (pos < source.length) {\n const ch = source[pos];\n const prevCh = pos > 0 ? source[pos - 1] : \"\";\n\n // Handle string literals\n if (!inString && (ch === '\"' || ch === \"'\")) {\n inString = true;\n stringChar = ch;\n } else if (inString && ch === stringChar && prevCh !== \"\\\\\") {\n inString = false;\n } else if (!inString) {\n // Track brackets/braces\n if (ch === \"[\") {\n depth++;\n foundArray = true;\n } else if (ch === \"]\") {\n depth--;\n if (depth === 0 && foundArray) {\n // Found the end of the array\n pos++;\n // Skip whitespace and include trailing comma if present\n while (pos < source.length && /\\s/.test(source[pos])) {\n pos++;\n }\n if (pos < source.length && source[pos] === \",\") {\n pos++;\n }\n return pos;\n }\n } else if (ch === \"{\" || ch === \"(\") {\n depth++;\n } else if (ch === \"}\" || ch === \")\") {\n depth--;\n } else if (!foundArray && depth === 0) {\n // Simple string value - ends at comma or closing brace\n if (ch === \",\" || ch === \"}\") {\n return pos + (ch === \",\" ? 1 : 0);\n }\n }\n }\n\n pos++;\n }\n\n return pos;\n}\n\n/**\n * Update existing uilint rule configurations with new options/severity\n *\n * This finds existing rule entries and replaces them with updated configurations.\n * Uses a more robust approach to handle multi-line rules with spread syntax.\n */\nfunction updateExistingRulesWithNewOptions(\n source: string,\n rulesToUpdate: RuleMetadata[]\n): string {\n if (rulesToUpdate.length === 0) return source;\n\n let updated = source;\n\n // Process rules in reverse order to avoid index shifting issues\n for (let i = rulesToUpdate.length - 1; i >= 0; i--) {\n const rule = rulesToUpdate[i]!;\n const ruleKeyPattern = new RegExp(\n `[\"']uilint/${rule.id.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}[\"']\\\\s*:`,\n \"g\"\n );\n\n // Find all occurrences (should only be one, but be safe)\n const matches: Array<{ index: number; length: number }> = [];\n let match;\n while ((match = ruleKeyPattern.exec(updated)) !== null) {\n if (match.index !== undefined) {\n matches.push({ index: match.index, length: match[0].length });\n }\n }\n\n // Process matches in reverse order\n for (let j = matches.length - 1; j >= 0; j--) {\n const keyMatch = matches[j]!;\n const keyStart = keyMatch.index;\n const keyEnd = keyStart + keyMatch.length;\n\n // Find the value start (after colon and whitespace)\n let valueStart = keyEnd;\n while (valueStart < updated.length && /\\s/.test(updated[valueStart])) {\n valueStart++;\n }\n\n // Find the value end\n const valueEnd = findRuleValueEnd(updated, valueStart);\n\n // Generate new rule config\n const newRuleConfig = generateSingleRuleConfig(rule);\n\n // Find the indentation of the rule key line\n const indent = detectIndent(updated, keyStart);\n\n // Replace the old rule with the new one\n const before = updated.slice(0, keyStart);\n const after = updated.slice(valueEnd);\n\n updated = before + newRuleConfig + \"\\n\" + indent + after;\n }\n }\n\n return updated;\n}\n\n/**\n * Inject uilint rules into the export default array\n */\nfunction injectUilintRules(\n source: string,\n selectedRules: RuleMetadata[]\n): { source: string; injected: boolean } {\n if (hasUilintRules(source)) {\n // Already has uilint rules - don't inject again\n return { source, injected: false };\n }\n\n const rulesConfig = generateRulesConfig(selectedRules);\n\n const configBlock = ` {\n files: [\n \"src/**/*.{js,jsx,ts,tsx}\",\n \"app/**/*.{js,jsx,ts,tsx}\",\n \"pages/**/*.{js,jsx,ts,tsx}\",\n ],\n plugins: { uilint: uilint },\n rules: {\n${rulesConfig}\n },\n },`;\n\n const arrayStart = findEsmExportedConfigArrayStartIndex(source);\n if (arrayStart === null) {\n return { source, injected: false };\n }\n\n const afterExport = source.slice(arrayStart);\n\n // Insert at the beginning of the array (after opening bracket)\n // Add a newline if the array doesn't start on a new line\n const needsNewline = !afterExport.trimStart().startsWith(\"\\n\");\n const insertion = needsNewline\n ? \"\\n\" + configBlock + \"\\n\"\n : configBlock + \"\\n\";\n\n return {\n source: source.slice(0, arrayStart) + insertion + source.slice(arrayStart),\n injected: true,\n };\n}\n\n/**\n * Inject uilint rules into the CommonJS export\n */\nfunction injectUilintRulesCommonJS(\n source: string,\n selectedRules: RuleMetadata[]\n): { source: string; injected: boolean } {\n if (hasUilintRules(source)) {\n return { source, injected: false };\n }\n\n const rulesConfig = generateRulesConfig(selectedRules);\n\n const configBlock = ` {\n files: [\n \"src/**/*.{js,jsx,ts,tsx}\",\n \"app/**/*.{js,jsx,ts,tsx}\",\n \"pages/**/*.{js,jsx,ts,tsx}\",\n ],\n plugins: { uilint: uilint },\n rules: {\n${rulesConfig}\n },\n },`;\n\n const arrayStart = findCommonJsExportedConfigArrayStartIndex(source);\n if (arrayStart === null) {\n return { source, injected: false };\n }\n\n const afterExport = source.slice(arrayStart);\n const needsNewline = !afterExport.trimStart().startsWith(\"\\n\");\n const insertion = needsNewline\n ? \"\\n\" + configBlock + \"\\n\"\n : configBlock + \"\\n\";\n\n return {\n source: source.slice(0, arrayStart) + insertion + source.slice(arrayStart),\n injected: true,\n };\n}\n\n/**\n * Install uilint-eslint into eslint config\n */\nexport async function installEslintPlugin(\n opts: InstallEslintPluginOptions\n): Promise<{\n configFile: string | null;\n modified: boolean;\n missingRuleIds: string[];\n configured: boolean;\n error?: string;\n}> {\n const configPath = findEslintConfigFile(opts.projectPath);\n\n if (!configPath) {\n return {\n configFile: null,\n modified: false,\n missingRuleIds: [],\n configured: false,\n };\n }\n\n const configFilename = getEslintConfigFilename(configPath);\n const original = readFileSync(configPath, \"utf-8\");\n const isCommonJS = configPath.endsWith(\".cjs\");\n\n const ast = getUilintEslintConfigInfoFromSourceAst(original);\n if (\"error\" in ast) {\n return {\n configFile: configFilename,\n modified: false,\n missingRuleIds: [],\n configured: false,\n error: ast.error,\n };\n }\n\n const { info, mod, arrayExpr, kind, existingConfig } = ast;\n const configuredIds = info.configuredRuleIds;\n\n const missingRules = getMissingSelectedRules(\n opts.selectedRules,\n configuredIds\n );\n const rulesToUpdate = getRulesNeedingUpdate(\n opts.selectedRules,\n configuredIds,\n arrayExpr\n );\n\n // Decide what rules to apply, respecting prompts.\n let rulesToApply: RuleMetadata[] = [];\n if (!info.configured) {\n rulesToApply = opts.selectedRules;\n } else {\n // When already configured, we only apply updates + missing rules.\n rulesToApply = [...missingRules, ...rulesToUpdate];\n if (missingRules.length > 0 && !opts.force) {\n const ok = await opts.confirmAddMissingRules?.(\n configFilename,\n missingRules\n );\n if (!ok) {\n return {\n configFile: configFilename,\n modified: false,\n missingRuleIds: missingRules.map((r) => r.id),\n configured: true,\n };\n }\n }\n }\n\n if (rulesToApply.length === 0) {\n return {\n configFile: configFilename,\n modified: false,\n missingRuleIds: missingRules.map((r) => r.id),\n configured: info.configured,\n };\n }\n\n let modifiedAst = false;\n\n // Check if .uilint/rules/ directory exists alongside this target package/app.\n // (Also allow a fallback to workspace root for backwards compatibility.)\n const localRulesDir = join(opts.projectPath, \".uilint\", \"rules\");\n const workspaceRoot = findWorkspaceRoot(opts.projectPath);\n const workspaceRulesDir = join(workspaceRoot, \".uilint\", \"rules\");\n\n const rulesRoot = existsSync(localRulesDir)\n ? opts.projectPath\n : workspaceRoot;\n\n // Always use local rules (they should have been copied by the plan phase)\n // For TypeScript configs, omit the extension (TypeScript will resolve .ts files)\n // For JavaScript configs, use .js extension\n // Note: We don't use .ts extension directly because it requires allowImportingTsExtensions\n const isTypeScriptConfig = configPath.endsWith(\".ts\");\n let fileExtension = isTypeScriptConfig ? \"\" : \".js\";\n\n let ruleImportNames: Map<string, string> | undefined;\n\n // Add imports for local rules\n if (kind === \"esm\") {\n const result = addLocalRuleImportsAst(\n mod,\n rulesToApply,\n configPath,\n rulesRoot,\n fileExtension,\n isTypeScriptConfig\n );\n ruleImportNames = result.importNames;\n if (result.changed) modifiedAst = true;\n } else {\n const result = addLocalRuleRequiresAst(\n mod.$ast,\n rulesToApply,\n configPath,\n rulesRoot,\n fileExtension,\n isTypeScriptConfig\n );\n ruleImportNames = result.importNames;\n if (result.changed) modifiedAst = true;\n }\n\n // Add config block with local rules (or update existing one)\n if (ruleImportNames && ruleImportNames.size > 0) {\n if (existingConfig.configObj !== null) {\n // Update the existing config block instead of creating a new one\n updateExistingUilintConfigBlock(\n existingConfig.configObj,\n rulesToApply,\n ruleImportNames\n );\n } else {\n // No existing config block, append a new one\n appendUilintConfigBlockToArray(arrayExpr, rulesToApply, ruleImportNames);\n }\n modifiedAst = true;\n }\n\n // Ensure uilint-eslint import for utilities (createRule, etc.)\n if (!info.configured) {\n if (kind === \"esm\") {\n // Import createRule utility from uilint-eslint\n mod.imports.$add({\n imported: \"createRule\",\n local: \"createRule\",\n from: \"uilint-eslint\",\n });\n modifiedAst = true;\n } else {\n // CommonJS: add require for createRule\n const stmtMod = parseModule(\n `const { createRule } = require(\"uilint-eslint\");`\n );\n const stmt = (stmtMod.$ast as any).body?.[0];\n if (stmt) {\n let insertAt = 0;\n const first = mod.$ast.body?.[0];\n if (\n first?.type === \"ExpressionStatement\" &&\n first.expression?.type === \"StringLiteral\" &&\n first.expression.value === \"use strict\"\n ) {\n insertAt = 1;\n }\n mod.$ast.body.splice(insertAt, 0, stmt);\n modifiedAst = true;\n }\n }\n }\n\n const updated = modifiedAst ? generateCode(mod).code : original;\n\n if (updated !== original) {\n writeFileSync(configPath, updated, \"utf-8\");\n return {\n configFile: configFilename,\n modified: true,\n missingRuleIds: missingRules.map((r) => r.id),\n configured: getUilintEslintConfigInfoFromSource(updated).configured,\n };\n }\n\n return {\n configFile: configFilename,\n modified: false,\n missingRuleIds: missingRules.map((r) => r.id),\n configured: getUilintEslintConfigInfoFromSource(updated).configured,\n };\n}\n\nexport interface RemoveEslintPluginOptions {\n projectPath: string;\n}\n\nexport interface RemoveEslintPluginResult {\n success: boolean;\n error?: string;\n modifiedFiles?: string[];\n}\n\n/**\n * Remove uilint-eslint rules from ESLint config\n *\n * This is a best-effort removal that:\n * 1. Removes uilint rule imports\n * 2. Removes uilint config blocks from the flat config array\n * 3. Removes uilint-eslint package import\n */\nexport async function removeEslintPlugin(\n options: RemoveEslintPluginOptions\n): Promise<RemoveEslintPluginResult> {\n const { projectPath } = options;\n\n const configPath = findEslintConfigFile(projectPath);\n if (!configPath) {\n return {\n success: true, // Nothing to uninstall\n modifiedFiles: [],\n };\n }\n\n try {\n const original = readFileSync(configPath, \"utf-8\");\n\n // Simple regex-based removal for now\n // Remove uilint rule imports (import { ... } from \"./uilint-rules/...\" or \"./.uilint/rules/...\")\n let updated = original.replace(\n /^import\\s+\\{[^}]*\\}\\s+from\\s+[\"'][^\"']*\\.uilint\\/rules[^\"']*[\"'];?\\s*$/gm,\n \"\"\n );\n\n // Remove default imports from .uilint/rules (e.g., import RuleName from \"./.uilint/rules/rule-name\")\n updated = updated.replace(\n /^import\\s+\\w+\\s+from\\s+[\"'][^\"']*\\.uilint\\/rules[^\"']*[\"'];?\\s*$/gm,\n \"\"\n );\n\n // Remove uilint-eslint import\n updated = updated.replace(\n /^import\\s+\\{[^}]*\\}\\s+from\\s+[\"']uilint-eslint[\"'];?\\s*$/gm,\n \"\"\n );\n\n // Remove createRule require\n updated = updated.replace(\n /^const\\s+\\{[^}]*createRule[^}]*\\}\\s*=\\s*require\\s*\\(\\s*[\"']uilint-eslint[\"']\\s*\\)\\s*;?\\s*$/gm,\n \"\"\n );\n\n // Remove uilint rules from rules objects (e.g., \"uilint/rule-name\": \"error\")\n updated = updated.replace(\n /[\"']uilint\\/[^\"']+[\"']\\s*:\\s*[\"'][^\"']+[\"']\\s*,?\\s*/g,\n \"\"\n );\n\n // Remove uilint rules array form (e.g., \"uilint/rule-name\": [\"error\", {}])\n updated = updated.replace(\n /[\"']uilint\\/[^\"']+[\"']\\s*:\\s*\\[[^\\]]*\\]\\s*,?\\s*/g,\n \"\"\n );\n\n // Remove entire uilint config block from flat config array\n // This matches: { plugins: { uilint: { rules: {...} } }, rules: {...} }\n updated = updated.replace(\n /\\{\\s*plugins:\\s*\\{\\s*uilint:\\s*\\{[^}]*\\}[^}]*\\}[^}]*rules:\\s*\\{[^}]*\\}[^}]*\\}\\s*,?\\s*/gs,\n \"\"\n );\n\n // Clean up empty lines\n updated = updated.replace(/\\n{3,}/g, \"\\n\\n\");\n\n if (updated !== original) {\n writeFileSync(configPath, updated, \"utf-8\");\n return {\n success: true,\n modifiedFiles: [configPath],\n };\n }\n\n return {\n success: true,\n modifiedFiles: [],\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nexport interface UpdateRuleConfigResult {\n success: boolean;\n error?: string;\n}\n\nexport interface RuleConfigFromFile {\n severity: \"error\" | \"warn\" | \"off\";\n options?: Record<string, unknown>;\n}\n\n/**\n * Extract severity from an AST node representing a rule value.\n * Handles both formats:\n * - Simple string: \"error\"\n * - Array: [\"error\", { ...options }]\n */\nfunction extractSeverityFromValueNode(\n valueNode: any\n): \"error\" | \"warn\" | \"off\" | null {\n if (!valueNode) return null;\n\n // Simple string format: \"error\"\n if (isStringLiteral(valueNode)) {\n const val = valueNode.value;\n if (val === \"error\" || val === \"warn\" || val === \"off\") {\n return val;\n }\n return null;\n }\n\n // Array format: [\"error\", { ...options }]\n if (valueNode.type === \"ArrayExpression\") {\n const firstEl = valueNode.elements?.[0];\n if (firstEl && isStringLiteral(firstEl)) {\n const val = firstEl.value;\n if (val === \"error\" || val === \"warn\" || val === \"off\") {\n return val;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract options object from an AST node representing a rule value in array format.\n * Returns undefined if not in array format or no options present.\n */\nfunction extractOptionsFromValueNode(\n valueNode: any\n): Record<string, unknown> | undefined {\n if (!valueNode || valueNode.type !== \"ArrayExpression\") return undefined;\n\n const elements = valueNode.elements ?? [];\n if (elements.length < 2) return undefined;\n\n const optionsNode = elements[1];\n if (!optionsNode || optionsNode.type !== \"ObjectExpression\") return undefined;\n\n // Convert AST object to plain JS object\n try {\n const obj: Record<string, unknown> = {};\n for (const prop of optionsNode.properties ?? []) {\n if (!prop || (prop.type !== \"ObjectProperty\" && prop.type !== \"Property\"))\n continue;\n\n const key = prop.key;\n let keyName: string | null = null;\n if (key?.type === \"Identifier\") {\n keyName = key.name;\n } else if (isStringLiteral(key)) {\n keyName = key.value;\n }\n if (!keyName) continue;\n\n const val = prop.value;\n if (isStringLiteral(val)) {\n obj[keyName] = val.value;\n } else if (\n val?.type === \"NumericLiteral\" ||\n (val?.type === \"Literal\" && typeof val.value === \"number\")\n ) {\n obj[keyName] = val.value;\n } else if (\n val?.type === \"BooleanLiteral\" ||\n (val?.type === \"Literal\" && typeof val.value === \"boolean\")\n ) {\n obj[keyName] = val.value;\n } else if (val?.type === \"ArrayExpression\") {\n // Simple array of literals\n const arr: unknown[] = [];\n for (const el of val.elements ?? []) {\n if (isStringLiteral(el)) arr.push(el.value);\n else if (\n el?.type === \"NumericLiteral\" ||\n (el?.type === \"Literal\" && typeof el.value === \"number\")\n )\n arr.push(el.value);\n else if (\n el?.type === \"BooleanLiteral\" ||\n (el?.type === \"Literal\" && typeof el.value === \"boolean\")\n )\n arr.push(el.value);\n }\n obj[keyName] = arr;\n }\n }\n return Object.keys(obj).length > 0 ? obj : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Read current rule configurations from an ESLint config file.\n * Uses magicast to parse the config and extract uilint rule severities and options.\n *\n * @param configPath Path to the ESLint config file\n * @returns Map of ruleId (without \"uilint/\" prefix) to { severity, options }\n */\nexport function readRuleConfigsFromConfig(\n configPath: string\n): Map<string, RuleConfigFromFile> {\n const configs = new Map<string, RuleConfigFromFile>();\n\n try {\n const source = readFileSync(configPath, \"utf-8\");\n const mod = parseModule(source);\n const found = findExportedConfigArrayExpression(mod);\n\n if (!found) {\n return configs;\n }\n\n const { arrayExpr } = found;\n const valueNodes = collectUilintRuleValueNodesFromConfigArray(arrayExpr);\n\n for (const [ruleId, valueNode] of valueNodes) {\n const severity = extractSeverityFromValueNode(valueNode);\n if (severity) {\n const options = extractOptionsFromValueNode(valueNode);\n configs.set(ruleId, { severity, options });\n }\n }\n } catch (error) {\n console.error(\"[eslint-config-inject] Failed to read rule configs:\", error);\n }\n\n return configs;\n}\n\n/**\n * Find a rule property node in the config array by rule ID.\n * Returns the property node and its parent rules object.\n */\nfunction findRulePropertyInConfigArray(\n arrayExpr: any,\n ruleId: string\n): { prop: any; rulesObj: any } | null {\n const fullRuleKey = `uilint/${ruleId}`;\n\n if (!arrayExpr || arrayExpr.type !== \"ArrayExpression\") return null;\n\n for (const el of arrayExpr.elements ?? []) {\n if (!el || el.type !== \"ObjectExpression\") continue;\n const rules = getObjectPropertyValue(el, \"rules\");\n if (!rules || rules.type !== \"ObjectExpression\") continue;\n\n for (const prop of rules.properties ?? []) {\n if (!prop) continue;\n if (prop.type !== \"ObjectProperty\" && prop.type !== \"Property\") continue;\n const key = prop.key;\n if (!isStringLiteral(key)) continue;\n if (key.value === fullRuleKey) {\n return { prop, rulesObj: rules };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Update the severity of a single uilint rule in the ESLint config file.\n * Uses magicast for proper AST-based editing.\n *\n * Handles both formats:\n * - Simple: \"uilint/rule-name\": \"error\"\n * - With options: \"uilint/rule-name\": [\"error\", { ... }]\n *\n * Only updates existing rules - returns error if rule not found.\n */\nexport function updateRuleSeverityInConfig(\n configPath: string,\n ruleId: string,\n severity: \"error\" | \"warn\" | \"off\"\n): UpdateRuleConfigResult {\n try {\n const source = readFileSync(configPath, \"utf-8\");\n const mod = parseModule(source);\n const found = findExportedConfigArrayExpression(mod);\n\n if (!found) {\n return {\n success: false,\n error: \"Could not parse ESLint config array\",\n };\n }\n\n const { arrayExpr } = found;\n const ruleInfo = findRulePropertyInConfigArray(arrayExpr, ruleId);\n\n if (!ruleInfo) {\n return {\n success: false,\n error: `Rule \"uilint/${ruleId}\" not found in config. Use 'uilint init' to add new rules.`,\n };\n }\n\n const { prop } = ruleInfo;\n const valueNode = prop.value;\n\n // Update the severity based on the format\n if (isStringLiteral(valueNode)) {\n // Simple string format - replace the value\n valueNode.value = severity;\n } else if (valueNode?.type === \"ArrayExpression\") {\n // Array format - update the first element\n const firstEl = valueNode.elements?.[0];\n if (firstEl && isStringLiteral(firstEl)) {\n firstEl.value = severity;\n } else {\n // Create a new string literal for severity\n const severityNode = (parseExpression(`\"${severity}\"`) as any).$ast;\n if (valueNode.elements && valueNode.elements.length > 0) {\n valueNode.elements[0] = severityNode;\n } else {\n valueNode.elements = [severityNode];\n }\n }\n } else {\n return {\n success: false,\n error: `Rule \"uilint/${ruleId}\" has unexpected format`,\n };\n }\n\n const updated = generateCode(mod).code;\n writeFileSync(configPath, updated, \"utf-8\");\n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Update the severity AND options of a single uilint rule in the ESLint config file.\n * Uses magicast for proper AST-based editing.\n *\n * Converts the rule to array format: [\"severity\", { ...options }]\n *\n * Only updates existing rules - returns error if rule not found.\n */\nexport function updateRuleConfigInConfig(\n configPath: string,\n ruleId: string,\n severity: \"error\" | \"warn\" | \"off\",\n options: Record<string, unknown>\n): UpdateRuleConfigResult {\n try {\n const source = readFileSync(configPath, \"utf-8\");\n const mod = parseModule(source);\n const found = findExportedConfigArrayExpression(mod);\n\n if (!found) {\n return {\n success: false,\n error: \"Could not parse ESLint config array\",\n };\n }\n\n const { arrayExpr } = found;\n const ruleInfo = findRulePropertyInConfigArray(arrayExpr, ruleId);\n\n if (!ruleInfo) {\n return {\n success: false,\n error: `Rule \"uilint/${ruleId}\" not found in config. Use 'uilint init' to add new rules.`,\n };\n }\n\n const { prop } = ruleInfo;\n\n // Build the new value as array format: [\"severity\", { ...options }]\n const optionsJson = JSON.stringify(options);\n const newValueExpr = `[\"${severity}\", ${optionsJson}]`;\n const newValueNode = (parseExpression(newValueExpr) as any).$ast;\n\n // Replace the value\n prop.value = newValueNode;\n\n const updated = generateCode(mod).code;\n writeFileSync(configPath, updated, \"utf-8\");\n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n","/**\n * Coverage preparation utilities.\n *\n * Handles automatically setting up coverage for vitest projects:\n * - Installing @vitest/coverage-v8 package\n * - Adding coverage config to vitest.config.ts\n * - Running tests with coverage to generate data\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport {\n detectPackageManager,\n installDependencies,\n runTestsWithCoverage,\n} from \"./package-manager.js\";\nimport { detectCoverageSetup, type CoverageSetupInfo } from \"./coverage-detect.js\";\n\nexport interface CoveragePreparationResult {\n /** Whether @vitest/coverage-v8 was added to package.json */\n packageAdded: boolean;\n /** Whether vitest.config.ts was modified to add coverage */\n configModified: boolean;\n /** Whether tests were run with coverage */\n testsRan: boolean;\n /** Whether coverage-final.json was generated */\n coverageGenerated: boolean;\n /** Total time taken in milliseconds */\n duration: number;\n /** Error message if something failed */\n error?: string;\n}\n\nexport interface PrepareCoverageOptions {\n /** Root directory of the app to prepare */\n appRoot: string;\n /** Progress callback for streaming updates */\n onProgress?: (message: string, phase: string) => void;\n /** Skip installing packages (useful in CI) */\n skipPackageInstall?: boolean;\n /** Skip running tests (useful when tests run separately) */\n skipTests?: boolean;\n}\n\n/**\n * Inject coverage configuration into vitest.config.ts\n * @returns true if config was modified, false if already configured or error\n */\nexport function injectCoverageConfig(vitestConfigPath: string): boolean {\n try {\n const content = readFileSync(vitestConfigPath, \"utf-8\");\n\n // Check if already has coverage config\n if (/coverage\\s*:\\s*\\{/.test(content)) {\n return false; // Already configured\n }\n\n // Find the `test: {` block and inject coverage after it\n // This handles patterns like:\n // - test: {\n // - test:{\n // - test : {\n const testBlockRegex = /(test\\s*:\\s*\\{)/;\n const match = content.match(testBlockRegex);\n\n if (!match) {\n // Can't find test block - might be a different config format\n return false;\n }\n\n const coverageConfig = `\n coverage: {\n provider: \"v8\",\n reporter: [\"text\", \"json\"],\n reportsDirectory: \"./coverage\",\n },`;\n\n // Insert coverage config right after `test: {`\n const newContent = content.replace(\n testBlockRegex,\n `$1${coverageConfig}`\n );\n\n writeFileSync(vitestConfigPath, newContent, \"utf-8\");\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Prepare a project for coverage generation.\n *\n * This function:\n * 1. Detects current coverage setup\n * 2. Installs @vitest/coverage-v8 if needed\n * 3. Adds coverage config to vitest.config.ts if needed\n * 4. Runs tests with coverage to generate data\n */\nexport async function prepareCoverage(\n options: PrepareCoverageOptions\n): Promise<CoveragePreparationResult> {\n const { appRoot, onProgress, skipPackageInstall, skipTests } = options;\n const start = Date.now();\n\n const result: CoveragePreparationResult = {\n packageAdded: false,\n configModified: false,\n testsRan: false,\n coverageGenerated: false,\n duration: 0,\n };\n\n try {\n // 1. Detect current setup\n onProgress?.(\"Detecting coverage setup...\", \"detect\");\n const setup = detectCoverageSetup(appRoot);\n\n // If no vitest, we can't set up coverage\n if (!setup.hasVitest) {\n result.error = \"Vitest not found in dependencies\";\n result.duration = Date.now() - start;\n return result;\n }\n\n // 2. Install coverage package if needed\n if (setup.needsCoveragePackage && !skipPackageInstall) {\n onProgress?.(\"Installing @vitest/coverage-v8...\", \"install\");\n const pm = detectPackageManager(appRoot);\n try {\n await installDependencies(pm, appRoot, [\"@vitest/coverage-v8\"]);\n result.packageAdded = true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n result.error = `Failed to install coverage package: ${msg}`;\n result.duration = Date.now() - start;\n return result;\n }\n }\n\n // 3. Add coverage config if needed\n if (setup.needsCoverageConfig && setup.vitestConfigPath) {\n onProgress?.(\"Adding coverage configuration...\", \"config\");\n result.configModified = injectCoverageConfig(setup.vitestConfigPath);\n }\n\n // 4. Run tests with coverage\n if (!skipTests) {\n // Re-check if we need to run tests\n const updatedSetup = detectCoverageSetup(appRoot);\n\n // Only run if:\n // - No coverage data exists, OR\n // - Config was just modified (data is stale)\n if (!updatedSetup.hasCoverageData || result.configModified) {\n onProgress?.(\"Running tests with coverage...\", \"test\");\n const pm = detectPackageManager(appRoot);\n try {\n await runTestsWithCoverage(pm, appRoot);\n result.testsRan = true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Tests might fail, but coverage might still be generated\n result.error = `Tests failed: ${msg}`;\n }\n\n // Check if coverage was generated\n const finalSetup = detectCoverageSetup(appRoot);\n result.coverageGenerated = finalSetup.hasCoverageData;\n }\n } else {\n onProgress?.(\"Skipping tests (skipTests=true)\", \"skip\");\n }\n\n result.duration = Date.now() - start;\n return result;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n result.error = `Coverage preparation failed: ${msg}`;\n result.duration = Date.now() - start;\n return result;\n }\n}\n\n/**\n * Check if coverage data needs to be regenerated.\n *\n * Returns true if:\n * - Coverage data doesn't exist\n * - Coverage package is missing\n * - Coverage config is missing\n */\nexport function needsCoveragePreparation(setup: CoverageSetupInfo): boolean {\n if (!setup.hasVitest) {\n return false; // Can't prepare without vitest\n }\n\n return (\n setup.needsCoveragePackage ||\n setup.needsCoverageConfig ||\n !setup.hasCoverageData\n );\n}\n"],"mappings":"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","names":["existsSync","join","existsSync","readFileSync","join","program","join","readFileSync","join","existsSync","readFileSync","writeFileSync","readFileSync","writeFileSync"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/rule-loader.ts"],"sourcesContent":["/**\n * Rule Loader Utility\n *\n * Loads ESLint rule source files from the uilint-eslint package for installation\n * into user projects. Rules are copied to .uilint/rules/ in the target project.\n */\n\nimport { readFileSync, existsSync, readdirSync } from \"fs\";\nimport { join, dirname } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { createRequire } from \"module\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst require = createRequire(import.meta.url);\n\nfunction findNodeModulesPackageRoot(\n pkgName: string,\n startDir: string\n): string | null {\n let dir = startDir;\n while (true) {\n const candidate = join(dir, \"node_modules\", pkgName);\n if (existsSync(join(candidate, \"package.json\"))) return candidate;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nfunction getUilintEslintPackageRoot(): string {\n // Prefer a filesystem-based lookup first. This avoids Node resolution edge\n // cases with package.json \"exports\" (especially ESM-only packages), and works\n // well for monorepos + pnpm where node_modules contains symlinks.\n //\n // Search upwards from process.cwd() (the user's project) and from this file\n // location (for test/dev environments).\n const fromCwd = findNodeModulesPackageRoot(\"uilint-eslint\", process.cwd());\n if (fromCwd) return fromCwd;\n\n const fromHere = findNodeModulesPackageRoot(\"uilint-eslint\", __dirname);\n if (fromHere) return fromHere;\n\n // Last resort: try resolver-based lookup.\n try {\n const entry = require.resolve(\"uilint-eslint\"); // typically .../dist/index.js\n const entryDir = dirname(entry); // typically .../dist\n return dirname(entryDir); // package root\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n throw new Error(\n `Unable to locate uilint-eslint in node_modules (searched upwards from cwd and uilint's install path).\\n` +\n `Resolver error: ${msg}\\n` +\n `Fix: ensure uilint-eslint is installed in the target project (or workspace) and try again.`\n );\n }\n}\n\n/**\n * Represents a file for a rule (implementation or test)\n */\nexport interface RuleFile {\n /** Relative path within the rules directory (e.g., \"no-arbitrary-tailwind.ts\") */\n relativePath: string;\n /** File content */\n content: string;\n}\n\n/**\n * Represents a complete rule ready for installation\n */\nexport interface RuleFiles {\n /** Rule identifier (e.g., \"no-arbitrary-tailwind\") */\n ruleId: string;\n /** Implementation file (main entry point - index.ts for directory rules, or single file) */\n implementation: RuleFile;\n /** Additional files for directory-based rules (lib/ utilities, etc.) */\n additionalFiles?: RuleFile[];\n /** Test file (if exists) */\n test?: RuleFile;\n}\n\n/**\n * Get the path to the uilint-eslint package source directory\n */\nfunction getUilintEslintSrcDir(): string {\n // In development: packages/uilint-eslint/src/\n // In production (installed): node_modules/uilint-eslint/src/\n\n // Try workspace/dev path first (repo layout)\n const devPath = join(\n __dirname,\n \"..\",\n \"..\",\n \"..\",\n \"..\",\n \"uilint-eslint\",\n \"src\"\n );\n if (existsSync(devPath)) return devPath;\n\n // Try from installed package root (works with \"exports\")\n const pkgRoot = getUilintEslintPackageRoot();\n const srcPath = join(pkgRoot, \"src\");\n if (existsSync(srcPath)) return srcPath;\n\n throw new Error(\n 'Could not find uilint-eslint \"src/\" directory. If you are using a published install of uilint-eslint, ensure it includes source files, or run a JS-only rules install.'\n );\n}\n\n/**\n * Get the path to the uilint-eslint package dist directory\n */\nfunction getUilintEslintDistDir(): string {\n // In development: packages/uilint-eslint/dist/\n // In production (installed): node_modules/uilint-eslint/dist/\n\n // Try workspace/dev path first (repo layout)\n const devPath = join(\n __dirname,\n \"..\",\n \"..\",\n \"..\",\n \"..\",\n \"uilint-eslint\",\n \"dist\"\n );\n if (existsSync(devPath)) return devPath;\n\n // Try from installed package root (works with \"exports\")\n const pkgRoot = getUilintEslintPackageRoot();\n const distPath = join(pkgRoot, \"dist\");\n if (existsSync(distPath)) return distPath;\n\n throw new Error(\n 'Could not find uilint-eslint \"dist/\" directory. This is a bug in uilint installation.'\n );\n}\n\n/**\n * All utilities that are exported from uilint-eslint and can be imported.\n * When adding a new utility to uilint-eslint that rules may import,\n * add it here AND export it from uilint-eslint/src/index.ts.\n *\n * Rules can also declare their dependencies via the `internalDependencies`\n * field in defineRuleMeta() - this serves as documentation and can be\n * used for validation.\n */\nconst EXPORTED_UTILITIES = [\n \"create-rule\",\n \"cache\",\n \"styleguide-loader\",\n \"import-graph\",\n \"component-parser\",\n \"export-resolver\",\n \"coverage-aggregator\",\n \"dependency-graph\",\n \"file-categorizer\",\n \"jsx-coverage-analyzer\",\n];\n\n/**\n * Maps internal export names to their uilint-eslint export names.\n * Some exports are renamed when re-exported from the main package\n * to avoid naming conflicts.\n *\n * Format: \"utilFile:internalName\" -> \"externalName\"\n */\nconst EXPORT_RENAMES: Record<string, string> = {\n \"import-graph:clearCache\": \"clearImportGraphCache\",\n};\n\n/**\n * Transform an import specifier, applying any necessary renames.\n * Handles both simple imports (foo) and aliased imports (foo as bar).\n */\nfunction transformImportSpecifier(specifier: string, utilFile: string): string {\n const trimmed = specifier.trim();\n\n // Check for aliased import: \"localName as alias\"\n const aliasMatch = trimmed.match(/^(\\w+)\\s+as\\s+(\\w+)$/);\n if (aliasMatch) {\n const [, localName, alias] = aliasMatch;\n const renameKey = `${utilFile}:${localName}`;\n const externalName = EXPORT_RENAMES[renameKey];\n if (externalName) {\n // If the alias matches the external name, simplify to just the name\n if (alias === externalName) {\n return externalName;\n }\n // Otherwise, use the external name with the original alias\n return `${externalName} as ${alias}`;\n }\n // No rename needed, keep original\n return trimmed;\n }\n\n // Simple import: check if it needs renaming\n const renameKey = `${utilFile}:${trimmed}`;\n const externalName = EXPORT_RENAMES[renameKey];\n if (externalName) {\n return externalName;\n }\n\n return trimmed;\n}\n\n/**\n * External packages that are re-exported from uilint-eslint.\n * When rules import these packages directly, transform them to import from uilint-eslint instead.\n * This ensures the dependencies are resolved correctly when rules are copied to user projects.\n */\nconst REEXPORTED_PACKAGES: Record<string, string[]> = {\n \"oxc-resolver\": [\"ResolverFactory\"],\n};\n\n/**\n * Transform rule content to fix imports for copied location\n * Changes imports from \"../utils/...\" or \"../../utils/...\" to \"uilint-eslint\"\n * Also transforms external package imports that are re-exported from uilint-eslint\n */\nfunction transformRuleContent(content: string): string {\n let transformed = content;\n\n // Replace all relative utility imports with uilint-eslint imports\n // Pattern: import { ... } from \"../utils/create-rule.js\" or \"../../utils/create-rule.js\"\n // This handles any combination of imports like { createRule, defineRuleMeta }\n transformed = transformed.replace(\n /import\\s+{([^}]+)}\\s+from\\s+[\"'](?:\\.\\.\\/)+utils\\/([^\"']+)\\.js[\"'];?/g,\n (match, imports, utilFile) => {\n if (EXPORTED_UTILITIES.includes(utilFile)) {\n // Transform each import specifier\n const specifiers = imports.split(\",\").map((s: string) =>\n transformImportSpecifier(s, utilFile)\n );\n return `import { ${specifiers.join(\", \")} } from \"uilint-eslint\";`;\n }\n return match; // Keep original if not a known utility\n }\n );\n\n // Also handle default imports: import createRule from \"../utils/create-rule.js\"\n transformed = transformed.replace(\n /import\\s+(\\w+)\\s+from\\s+[\"'](?:\\.\\.\\/)+utils\\/([^\"']+)\\.js[\"'];?/g,\n (match, importName, utilFile) => {\n if (EXPORTED_UTILITIES.includes(utilFile)) {\n return `import { ${importName} } from \"uilint-eslint\";`;\n }\n return match;\n }\n );\n\n // Transform external package imports that are re-exported from uilint-eslint\n // Pattern: import { ResolverFactory } from \"oxc-resolver\"\n for (const [pkgName, exports] of Object.entries(REEXPORTED_PACKAGES)) {\n const escapedPkgName = pkgName.replace(/-/g, \"\\\\-\");\n const regex = new RegExp(\n `import\\\\s+{([^}]+)}\\\\s+from\\\\s+[\"']${escapedPkgName}[\"'];?`,\n \"g\"\n );\n transformed = transformed.replace(regex, (match, imports) => {\n const importNames = imports.split(\",\").map((s: string) => s.trim());\n // Only transform if all imported names are re-exported from uilint-eslint\n const allReexported = importNames.every((name: string) => {\n // Handle \"Foo as Bar\" syntax - extract the original name\n const originalName = name.split(/\\s+as\\s+/)[0].trim();\n return exports.includes(originalName);\n });\n if (allReexported) {\n return `import { ${imports} } from \"uilint-eslint\";`;\n }\n return match;\n });\n }\n\n return transformed;\n}\n\n/**\n * Check if a rule is directory-based (has index.ts/index.js) or single-file\n */\nfunction isDirectoryBasedRule(rulesDir: string, ruleId: string): boolean {\n const ruleDir = join(rulesDir, ruleId);\n return existsSync(ruleDir) && existsSync(join(ruleDir, \"index.ts\"));\n}\n\n/**\n * Load all files from a directory-based rule\n */\nfunction loadDirectoryRule(\n rulesDir: string,\n ruleId: string\n): { files: RuleFile[]; testFile?: RuleFile } {\n const ruleDir = join(rulesDir, ruleId);\n const files: RuleFile[] = [];\n let testFile: RuleFile | undefined;\n\n function collectFiles(dir: string, relativeTo: string): void {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n const relativePath = join(relativeTo, entry.name);\n\n if (entry.isDirectory()) {\n collectFiles(fullPath, relativePath);\n } else if (entry.name.endsWith(\".ts\")) {\n if (entry.name.endsWith(\".test.ts\")) {\n // Handle test file separately\n testFile = {\n relativePath,\n content: transformRuleContent(readFileSync(fullPath, \"utf-8\")),\n };\n } else {\n files.push({\n relativePath,\n content: transformRuleContent(readFileSync(fullPath, \"utf-8\")),\n });\n }\n }\n }\n }\n\n collectFiles(ruleDir, ruleId);\n return { files, testFile };\n}\n\n/**\n * Load a specific rule by ID\n */\nexport function loadRule(\n ruleId: string,\n options: { typescript: boolean } = { typescript: true }\n): RuleFiles {\n const { typescript } = options;\n const extension = typescript ? \".ts\" : \".js\";\n\n if (typescript) {\n // Load TypeScript source files\n const rulesDir = join(getUilintEslintSrcDir(), \"rules\");\n\n // Check if this is a directory-based rule\n if (isDirectoryBasedRule(rulesDir, ruleId)) {\n const { files, testFile } = loadDirectoryRule(rulesDir, ruleId);\n\n if (files.length === 0) {\n throw new Error(`Rule \"${ruleId}\" directory exists but contains no TypeScript files`);\n }\n\n // Find the index.ts as the main implementation\n const indexFile = files.find((f) => f.relativePath === join(ruleId, \"index.ts\"));\n if (!indexFile) {\n throw new Error(`Rule \"${ruleId}\" directory missing index.ts`);\n }\n\n return {\n ruleId,\n implementation: indexFile,\n additionalFiles: files.filter((f) => f !== indexFile),\n test: testFile,\n };\n }\n\n // Single-file rule\n const implPath = join(rulesDir, `${ruleId}.ts`);\n const testPath = join(rulesDir, `${ruleId}.test.ts`);\n\n if (!existsSync(implPath)) {\n throw new Error(`Rule \"${ruleId}\" not found at ${implPath}`);\n }\n\n const rawContent = readFileSync(implPath, \"utf-8\");\n const transformedContent = transformRuleContent(rawContent);\n\n const implementation: RuleFile = {\n relativePath: `${ruleId}.ts`,\n content: transformedContent,\n };\n\n const test: RuleFile | undefined = existsSync(testPath)\n ? {\n relativePath: `${ruleId}.test.ts`,\n content: transformRuleContent(readFileSync(testPath, \"utf-8\")),\n }\n : undefined;\n\n return {\n ruleId,\n implementation,\n test,\n };\n } else {\n // Load compiled JavaScript files\n const rulesDir = join(getUilintEslintDistDir(), \"rules\");\n const implPath = join(rulesDir, `${ruleId}.js`);\n\n if (!existsSync(implPath)) {\n throw new Error(\n `Rule \"${ruleId}\" not found at ${implPath}. ` +\n `For JavaScript-only projects, uilint-eslint must be built to include compiled rule files in dist/rules/. ` +\n `If you're developing uilint-eslint, run 'pnpm build' in packages/uilint-eslint. ` +\n `If you're using a published package, ensure it includes the dist/ directory.`\n );\n }\n\n // Compiled JS files don't need transformation - they already use uilint-eslint imports\n const content = readFileSync(implPath, \"utf-8\");\n\n const implementation: RuleFile = {\n relativePath: `${ruleId}.js`,\n content,\n };\n\n // Test files are not compiled, so we don't copy them for JS projects\n return {\n ruleId,\n implementation,\n };\n }\n}\n\n/**\n * Load multiple rules by their IDs\n */\nexport function loadSelectedRules(\n ruleIds: string[],\n options: { typescript: boolean } = { typescript: true }\n): RuleFiles[] {\n return ruleIds.map((id) => loadRule(id, options));\n}\n\n/**\n * Get the list of available rule IDs from the registry\n */\nexport function getAvailableRuleIds(): string[] {\n try {\n // Import the rule registry from uilint-eslint\n const { ruleRegistry } = require(\"uilint-eslint\");\n return ruleRegistry.map((rule: { id: string }) => rule.id);\n } catch {\n // Fallback: try to read from filesystem\n const rulesDir = join(getUilintEslintSrcDir(), \"rules\");\n if (!existsSync(rulesDir)) {\n return [];\n }\n\n // This is a fallback - ideally we'd use the registry\n // But if we can't import it, we can at least try to list files\n const files = readdirSync(rulesDir);\n return files\n .filter((f: string) => f.endsWith(\".ts\") && !f.endsWith(\".test.ts\"))\n .map((f: string) => f.replace(\".ts\", \"\"));\n }\n}\n"],"mappings":";;;AAOA,SAAS,cAAc,YAAY,mBAAmB;AACtD,SAAS,MAAM,eAAe;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAE9B,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAY,QAAQ,UAAU;AACpC,IAAMA,WAAU,cAAc,YAAY,GAAG;AAE7C,SAAS,2BACP,SACA,UACe;AACf,MAAI,MAAM;AACV,SAAO,MAAM;AACX,UAAM,YAAY,KAAK,KAAK,gBAAgB,OAAO;AACnD,QAAI,WAAW,KAAK,WAAW,cAAc,CAAC,EAAG,QAAO;AACxD,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,SAAS,6BAAqC;AAO5C,QAAM,UAAU,2BAA2B,iBAAiB,QAAQ,IAAI,CAAC;AACzE,MAAI,QAAS,QAAO;AAEpB,QAAM,WAAW,2BAA2B,iBAAiB,SAAS;AACtE,MAAI,SAAU,QAAO;AAGrB,MAAI;AACF,UAAM,QAAQA,SAAQ,QAAQ,eAAe;AAC7C,UAAM,WAAW,QAAQ,KAAK;AAC9B,WAAO,QAAQ,QAAQ;AAAA,EACzB,SAAS,GAAG;AACV,UAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,UAAM,IAAI;AAAA,MACR;AAAA,kBACqB,GAAG;AAAA;AAAA,IAE1B;AAAA,EACF;AACF;AA6BA,SAAS,wBAAgC;AAKvC,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,MAAI,WAAW,OAAO,EAAG,QAAO;AAGhC,QAAM,UAAU,2BAA2B;AAC3C,QAAM,UAAU,KAAK,SAAS,KAAK;AACnC,MAAI,WAAW,OAAO,EAAG,QAAO;AAEhC,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAKA,SAAS,yBAAiC;AAKxC,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,MAAI,WAAW,OAAO,EAAG,QAAO;AAGhC,QAAM,UAAU,2BAA2B;AAC3C,QAAM,WAAW,KAAK,SAAS,MAAM;AACrC,MAAI,WAAW,QAAQ,EAAG,QAAO;AAEjC,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAWA,IAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASA,IAAM,iBAAyC;AAAA,EAC7C,2BAA2B;AAC7B;AAMA,SAAS,yBAAyB,WAAmB,UAA0B;AAC7E,QAAM,UAAU,UAAU,KAAK;AAG/B,QAAM,aAAa,QAAQ,MAAM,sBAAsB;AACvD,MAAI,YAAY;AACd,UAAM,CAAC,EAAE,WAAW,KAAK,IAAI;AAC7B,UAAMC,aAAY,GAAG,QAAQ,IAAI,SAAS;AAC1C,UAAMC,gBAAe,eAAeD,UAAS;AAC7C,QAAIC,eAAc;AAEhB,UAAI,UAAUA,eAAc;AAC1B,eAAOA;AAAA,MACT;AAEA,aAAO,GAAGA,aAAY,OAAO,KAAK;AAAA,IACpC;AAEA,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,GAAG,QAAQ,IAAI,OAAO;AACxC,QAAM,eAAe,eAAe,SAAS;AAC7C,MAAI,cAAc;AAChB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAOA,IAAM,sBAAgD;AAAA,EACpD,gBAAgB,CAAC,iBAAiB;AACpC;AAOA,SAAS,qBAAqB,SAAyB;AACrD,MAAI,cAAc;AAKlB,gBAAc,YAAY;AAAA,IACxB;AAAA,IACA,CAAC,OAAO,SAAS,aAAa;AAC5B,UAAI,mBAAmB,SAAS,QAAQ,GAAG;AAEzC,cAAM,aAAa,QAAQ,MAAM,GAAG,EAAE;AAAA,UAAI,CAAC,MACzC,yBAAyB,GAAG,QAAQ;AAAA,QACtC;AACA,eAAO,YAAY,WAAW,KAAK,IAAI,CAAC;AAAA,MAC1C;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAGA,gBAAc,YAAY;AAAA,IACxB;AAAA,IACA,CAAC,OAAO,YAAY,aAAa;AAC/B,UAAI,mBAAmB,SAAS,QAAQ,GAAG;AACzC,eAAO,YAAY,UAAU;AAAA,MAC/B;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAIA,aAAW,CAAC,SAAS,OAAO,KAAK,OAAO,QAAQ,mBAAmB,GAAG;AACpE,UAAM,iBAAiB,QAAQ,QAAQ,MAAM,KAAK;AAClD,UAAM,QAAQ,IAAI;AAAA,MAChB,sCAAsC,cAAc;AAAA,MACpD;AAAA,IACF;AACA,kBAAc,YAAY,QAAQ,OAAO,CAAC,OAAO,YAAY;AAC3D,YAAM,cAAc,QAAQ,MAAM,GAAG,EAAE,IAAI,CAAC,MAAc,EAAE,KAAK,CAAC;AAElE,YAAM,gBAAgB,YAAY,MAAM,CAAC,SAAiB;AAExD,cAAM,eAAe,KAAK,MAAM,UAAU,EAAE,CAAC,EAAE,KAAK;AACpD,eAAO,QAAQ,SAAS,YAAY;AAAA,MACtC,CAAC;AACD,UAAI,eAAe;AACjB,eAAO,YAAY,OAAO;AAAA,MAC5B;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAKA,SAAS,qBAAqB,UAAkB,QAAyB;AACvE,QAAM,UAAU,KAAK,UAAU,MAAM;AACrC,SAAO,WAAW,OAAO,KAAK,WAAW,KAAK,SAAS,UAAU,CAAC;AACpE;AAKA,SAAS,kBACP,UACA,QAC4C;AAC5C,QAAM,UAAU,KAAK,UAAU,MAAM;AACrC,QAAM,QAAoB,CAAC;AAC3B,MAAI;AAEJ,WAAS,aAAa,KAAa,YAA0B;AAC3D,UAAM,UAAU,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AACxD,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAW,KAAK,KAAK,MAAM,IAAI;AACrC,YAAM,eAAe,KAAK,YAAY,MAAM,IAAI;AAEhD,UAAI,MAAM,YAAY,GAAG;AACvB,qBAAa,UAAU,YAAY;AAAA,MACrC,WAAW,MAAM,KAAK,SAAS,KAAK,GAAG;AACrC,YAAI,MAAM,KAAK,SAAS,UAAU,GAAG;AAEnC,qBAAW;AAAA,YACT;AAAA,YACA,SAAS,qBAAqB,aAAa,UAAU,OAAO,CAAC;AAAA,UAC/D;AAAA,QACF,OAAO;AACL,gBAAM,KAAK;AAAA,YACT;AAAA,YACA,SAAS,qBAAqB,aAAa,UAAU,OAAO,CAAC;AAAA,UAC/D,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,eAAa,SAAS,MAAM;AAC5B,SAAO,EAAE,OAAO,SAAS;AAC3B;AAKO,SAAS,SACd,QACA,UAAmC,EAAE,YAAY,KAAK,GAC3C;AACX,QAAM,EAAE,WAAW,IAAI;AACvB,QAAM,YAAY,aAAa,QAAQ;AAEvC,MAAI,YAAY;AAEd,UAAM,WAAW,KAAK,sBAAsB,GAAG,OAAO;AAGtD,QAAI,qBAAqB,UAAU,MAAM,GAAG;AAC1C,YAAM,EAAE,OAAO,SAAS,IAAI,kBAAkB,UAAU,MAAM;AAE9D,UAAI,MAAM,WAAW,GAAG;AACtB,cAAM,IAAI,MAAM,SAAS,MAAM,qDAAqD;AAAA,MACtF;AAGA,YAAM,YAAY,MAAM,KAAK,CAAC,MAAM,EAAE,iBAAiB,KAAK,QAAQ,UAAU,CAAC;AAC/E,UAAI,CAAC,WAAW;AACd,cAAM,IAAI,MAAM,SAAS,MAAM,8BAA8B;AAAA,MAC/D;AAEA,aAAO;AAAA,QACL;AAAA,QACA,gBAAgB;AAAA,QAChB,iBAAiB,MAAM,OAAO,CAAC,MAAM,MAAM,SAAS;AAAA,QACpD,MAAM;AAAA,MACR;AAAA,IACF;AAGA,UAAM,WAAW,KAAK,UAAU,GAAG,MAAM,KAAK;AAC9C,UAAM,WAAW,KAAK,UAAU,GAAG,MAAM,UAAU;AAEnD,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,YAAM,IAAI,MAAM,SAAS,MAAM,kBAAkB,QAAQ,EAAE;AAAA,IAC7D;AAEA,UAAM,aAAa,aAAa,UAAU,OAAO;AACjD,UAAM,qBAAqB,qBAAqB,UAAU;AAE1D,UAAM,iBAA2B;AAAA,MAC/B,cAAc,GAAG,MAAM;AAAA,MACvB,SAAS;AAAA,IACX;AAEA,UAAM,OAA6B,WAAW,QAAQ,IAClD;AAAA,MACE,cAAc,GAAG,MAAM;AAAA,MACvB,SAAS,qBAAqB,aAAa,UAAU,OAAO,CAAC;AAAA,IAC/D,IACA;AAEJ,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,OAAO;AAEL,UAAM,WAAW,KAAK,uBAAuB,GAAG,OAAO;AACvD,UAAM,WAAW,KAAK,UAAU,GAAG,MAAM,KAAK;AAE9C,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,SAAS,MAAM,kBAAkB,QAAQ;AAAA,MAI3C;AAAA,IACF;AAGA,UAAM,UAAU,aAAa,UAAU,OAAO;AAE9C,UAAM,iBAA2B;AAAA,MAC/B,cAAc,GAAG,MAAM;AAAA,MACvB;AAAA,IACF;AAGA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,kBACd,SACA,UAAmC,EAAE,YAAY,KAAK,GACzC;AACb,SAAO,QAAQ,IAAI,CAAC,OAAO,SAAS,IAAI,OAAO,CAAC;AAClD;","names":["require","renameKey","externalName"]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|