@pyreon/lint 0.12.13 → 0.12.14

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.
Files changed (45) hide show
  1. package/README.md +55 -2
  2. package/lib/analysis/cli.js.html +1 -1
  3. package/lib/analysis/index.js.html +1 -1
  4. package/lib/cli.js +960 -162
  5. package/lib/cli.js.map +1 -1
  6. package/lib/index.js +935 -161
  7. package/lib/index.js.map +1 -1
  8. package/lib/types/index.d.ts +96 -23
  9. package/lib/types/index.d.ts.map +1 -1
  10. package/package.json +2 -1
  11. package/schema/pyreonlintrc.schema.json +64 -0
  12. package/src/cli.ts +44 -2
  13. package/src/config/presets.ts +13 -1
  14. package/src/index.ts +7 -0
  15. package/src/lint.ts +37 -6
  16. package/src/lsp/index.ts +15 -2
  17. package/src/rules/architecture/dev-guard-warnings.ts +172 -17
  18. package/src/rules/architecture/no-circular-import.ts +7 -0
  19. package/src/rules/architecture/no-process-dev-gate.ts +18 -45
  20. package/src/rules/architecture/require-browser-smoke-test.ts +227 -0
  21. package/src/rules/form/no-submit-without-validation.ts +9 -0
  22. package/src/rules/form/no-unregistered-field.ts +9 -0
  23. package/src/rules/hooks/no-raw-addeventlistener.ts +7 -0
  24. package/src/rules/hooks/no-raw-localstorage.ts +12 -1
  25. package/src/rules/hooks/no-raw-setinterval.ts +14 -0
  26. package/src/rules/index.ts +4 -1
  27. package/src/rules/jsx/no-props-destructure.ts +20 -6
  28. package/src/rules/lifecycle/no-dom-in-setup.ts +67 -7
  29. package/src/rules/reactivity/no-bare-signal-in-jsx.ts +12 -1
  30. package/src/rules/reactivity/no-unbatched-updates.ts +3 -0
  31. package/src/rules/router/no-imperative-navigate-in-render.ts +131 -35
  32. package/src/rules/ssr/no-window-in-ssr.ts +418 -35
  33. package/src/rules/store/no-duplicate-store-id.ts +11 -0
  34. package/src/rules/store/no-mutate-store-state.ts +11 -1
  35. package/src/rules/styling/no-dynamic-styled.ts +13 -24
  36. package/src/rules/styling/no-theme-outside-provider.ts +34 -2
  37. package/src/runner.ts +100 -10
  38. package/src/tests/runner.test.ts +1573 -21
  39. package/src/types.ts +74 -3
  40. package/src/utils/component-context.ts +106 -0
  41. package/src/utils/exempt-paths.ts +39 -0
  42. package/src/utils/file-roles.ts +32 -0
  43. package/src/utils/imports.ts +4 -1
  44. package/src/utils/validate-options.ts +68 -0
  45. package/src/watcher.ts +17 -0
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/cache.ts","../src/config/ignore.ts","../src/config/loader.ts","../src/utils/imports.ts","../src/utils/ast.ts","../src/rules/accessibility/dialog-a11y.ts","../src/rules/accessibility/overlay-a11y.ts","../src/rules/accessibility/toast-a11y.ts","../src/rules/architecture/dev-guard-warnings.ts","../src/rules/architecture/no-circular-import.ts","../src/rules/architecture/no-cross-layer-import.ts","../src/rules/architecture/no-deep-import.ts","../src/rules/architecture/no-error-without-prefix.ts","../src/rules/architecture/no-process-dev-gate.ts","../src/rules/form/no-submit-without-validation.ts","../src/rules/form/no-unregistered-field.ts","../src/rules/form/prefer-field-array.ts","../src/rules/hooks/no-raw-addeventlistener.ts","../src/rules/hooks/no-raw-localstorage.ts","../src/rules/hooks/no-raw-setinterval.ts","../src/rules/jsx/no-and-conditional.ts","../src/rules/jsx/no-children-access.ts","../src/rules/jsx/no-classname.ts","../src/rules/jsx/no-htmlfor.ts","../src/rules/jsx/no-index-as-by.ts","../src/rules/jsx/no-map-in-jsx.ts","../src/rules/jsx/no-missing-for-by.ts","../src/rules/jsx/no-onchange.ts","../src/rules/jsx/no-props-destructure.ts","../src/rules/jsx/no-ternary-conditional.ts","../src/rules/jsx/use-by-not-key.ts","../src/rules/lifecycle/no-dom-in-setup.ts","../src/rules/lifecycle/no-effect-in-mount.ts","../src/rules/lifecycle/no-missing-cleanup.ts","../src/rules/lifecycle/no-mount-in-effect.ts","../src/rules/performance/no-eager-import.ts","../src/rules/performance/no-effect-in-for.ts","../src/rules/performance/no-large-for-without-by.ts","../src/rules/performance/prefer-show-over-display.ts","../src/rules/reactivity/no-bare-signal-in-jsx.ts","../src/rules/reactivity/no-context-destructure.ts","../src/rules/reactivity/no-effect-assignment.ts","../src/rules/reactivity/no-nested-effect.ts","../src/rules/reactivity/no-peek-in-tracked.ts","../src/rules/reactivity/no-signal-in-loop.ts","../src/rules/reactivity/no-signal-in-props.ts","../src/rules/reactivity/no-signal-leak.ts","../src/rules/reactivity/no-unbatched-updates.ts","../src/rules/reactivity/prefer-computed.ts","../src/rules/router/no-href-navigation.ts","../src/rules/router/no-imperative-navigate-in-render.ts","../src/rules/router/no-missing-fallback.ts","../src/rules/router/prefer-use-is-active.ts","../src/rules/ssr/no-mismatch-risk.ts","../src/rules/ssr/no-window-in-ssr.ts","../src/rules/ssr/prefer-request-context.ts","../src/rules/store/no-duplicate-store-id.ts","../src/rules/store/no-mutate-store-state.ts","../src/rules/store/no-store-outside-provider.ts","../src/rules/styling/no-dynamic-styled.ts","../src/rules/styling/no-inline-style-object.ts","../src/rules/styling/no-theme-outside-provider.ts","../src/rules/styling/prefer-cx.ts","../src/rules/index.ts","../src/config/presets.ts","../src/utils/source.ts","../src/utils/index.ts","../src/runner.ts","../src/lint.ts","../src/reporter.ts","../src/lsp/index.ts","../src/watcher.ts"],"sourcesContent":["import type { LineIndex } from './utils/source'\n\n/**\n * Simple in-memory cache for parsed ASTs keyed by file content hash.\n *\n * Uses FNV-1a hash of source text as cache key for fast lookups\n * during repeat runs (e.g., watch mode).\n *\n * @example\n * ```ts\n * import { AstCache } from \"@pyreon/lint\"\n *\n * const cache = new AstCache()\n * const cached = cache.get(sourceText)\n * if (!cached) {\n * const parsed = parse(sourceText)\n * cache.set(sourceText, parsed)\n * }\n * ```\n */\nexport class AstCache {\n private cache = new Map<string, { program: any; lineIndex: LineIndex }>()\n\n get(sourceText: string): { program: any; lineIndex: LineIndex } | undefined {\n const key = fnv1aHash(sourceText)\n return this.cache.get(key)\n }\n\n set(sourceText: string, value: { program: any; lineIndex: LineIndex }): void {\n const key = fnv1aHash(sourceText)\n this.cache.set(key, value)\n }\n\n clear(): void {\n this.cache.clear()\n }\n\n get size(): number {\n return this.cache.size\n }\n}\n\n/** FNV-1a hash — fast, non-cryptographic, low collision rate. */\nfunction fnv1aHash(str: string): string {\n let hash = 0x811c9dc5 // FNV offset basis\n for (let i = 0; i < str.length; i++) {\n hash ^= str.charCodeAt(i)\n hash = (hash * 0x01000193) | 0 // FNV prime, keep 32-bit\n }\n return (hash >>> 0).toString(36)\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { join, relative, resolve } from 'node:path'\n\n/**\n * Create a filter function that returns true if a file path should be ignored.\n *\n * Loads patterns from `.pyreonlintignore` and `.gitignore` in the given directory.\n *\n * @example\n * ```ts\n * import { createIgnoreFilter } from \"@pyreon/lint\"\n *\n * const isIgnored = createIgnoreFilter(process.cwd())\n * if (!isIgnored(\"src/app.tsx\")) lintFile(...)\n * ```\n */\nexport function createIgnoreFilter(\n cwd: string,\n extraIgnore?: string | undefined,\n): (filePath: string) => boolean {\n const patterns: string[] = []\n const resolvedCwd = resolve(cwd)\n\n // Load .pyreonlintignore\n loadPatternsFromFile(join(resolvedCwd, '.pyreonlintignore'), patterns)\n\n // Load .gitignore\n loadPatternsFromFile(join(resolvedCwd, '.gitignore'), patterns)\n\n // Load extra ignore file if provided\n if (extraIgnore) {\n loadPatternsFromFile(resolve(extraIgnore), patterns)\n }\n\n // Compile patterns into matchers\n const matchers = patterns.map((p) => compileMatcher(p))\n\n return (filePath: string): boolean => {\n const rel = relative(resolvedCwd, resolve(filePath))\n // Normalize to forward slashes\n const normalized = rel.replace(/\\\\/g, '/')\n\n for (const matcher of matchers) {\n if (matcher(normalized)) return true\n }\n return false\n }\n}\n\nfunction loadPatternsFromFile(filePath: string, patterns: string[]): void {\n if (!existsSync(filePath)) return\n try {\n const content = readFileSync(filePath, 'utf-8')\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n // Skip empty lines and comments\n if (!trimmed || trimmed.startsWith('#')) continue\n patterns.push(trimmed)\n }\n } catch {\n // Ignore read errors\n }\n}\n\n/**\n * Compile a gitignore-style pattern into a matcher function.\n * Supports: `*` (any non-slash chars), `**` (any path segment), `?` (single char),\n * leading `/` (root-anchored), trailing `/` (directory only).\n */\nfunction compileMatcher(pattern: string): (path: string) => boolean {\n let p = pattern\n let anchored = false\n\n // Negated patterns (not supported — just skip them)\n if (p.startsWith('!')) {\n return () => false\n }\n\n // Leading slash means anchored to root\n if (p.startsWith('/')) {\n anchored = true\n p = p.slice(1)\n }\n\n // Trailing slash means only match directories (we treat all paths as files, so strip it\n // and match as a prefix)\n let dirOnly = false\n if (p.endsWith('/')) {\n dirOnly = true\n p = p.slice(0, -1)\n }\n\n const regex = globToRegex(p)\n\n return (path: string): boolean => {\n if (dirOnly) {\n // Match as prefix: the pattern should match a directory portion\n if (anchored) {\n return regex.test(path) || path.startsWith(`${p}/`) || path === p\n }\n // Unanchored directory pattern — match anywhere in path\n return regex.test(path) || path.includes(`/${p}/`) || path.startsWith(`${p}/`) || path === p\n }\n\n if (anchored) {\n return regex.test(path)\n }\n\n // Unanchored pattern — try matching the full path, or just the basename\n if (regex.test(path)) return true\n\n // Also try matching against just the filename\n const lastSlash = path.lastIndexOf('/')\n if (lastSlash !== -1) {\n const basename = path.slice(lastSlash + 1)\n return regex.test(basename)\n }\n\n return false\n }\n}\n\nconst GLOB_CHAR_MAP: Record<string, string> = {\n '?': '[^/]',\n '.': '\\\\.',\n '/': '/',\n}\n\nfunction handleStar(glob: string, pos: number): { pattern: string; advance: number } {\n if (glob[pos + 1] === '*') {\n if (glob[pos + 2] === '/') return { pattern: '(?:.*/)?', advance: 3 }\n return { pattern: '.*', advance: 2 }\n }\n return { pattern: '[^/]*', advance: 1 }\n}\n\nfunction globToRegex(glob: string): RegExp {\n let result = '^'\n let i = 0\n\n while (i < glob.length) {\n const ch = glob[i] as string\n if (ch === '*') {\n const star = handleStar(glob, i)\n result += star.pattern\n i += star.advance\n } else {\n result += GLOB_CHAR_MAP[ch] ?? escapeRegex(ch)\n i++\n }\n }\n\n result += '$'\n return new RegExp(result)\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[\\\\^$+{}[\\]|()]/g, '\\\\$&')\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport type { LintConfigFile } from '../types'\n\nconst CONFIG_FILENAMES = ['.pyreonlintrc.json', '.pyreonlintrc', 'pyreonlint.config.json']\n\n/**\n * Load a lint config file from the given directory or its parents.\n *\n * Search order:\n * 1. `.pyreonlintrc.json`\n * 2. `.pyreonlintrc`\n * 3. `pyreonlint.config.json`\n * 4. `package.json` `\"pyreonlint\"` field\n *\n * @example\n * ```ts\n * import { loadConfig } from \"@pyreon/lint\"\n *\n * const config = loadConfig(process.cwd())\n * if (config) console.log(config.preset)\n * ```\n */\nexport function loadConfig(cwd: string): LintConfigFile | null {\n let dir = resolve(cwd)\n const root = dirname(dir)\n\n while (true) {\n const found = searchDirectory(dir)\n if (found !== null) return found\n\n const parent = dirname(dir)\n if (parent === dir || parent === root) break\n dir = parent\n }\n\n return null\n}\n\nfunction searchDirectory(dir: string): LintConfigFile | null {\n for (const filename of CONFIG_FILENAMES) {\n const content = tryReadJson(join(dir, filename))\n if (content !== null) return content\n }\n return extractPkgConfig(join(dir, 'package.json'))\n}\n\nfunction extractPkgConfig(pkgPath: string): LintConfigFile | null {\n const pkg = tryReadJson(pkgPath)\n if (pkg === null || typeof pkg !== 'object' || !('pyreonlint' in pkg)) return null\n const field = (pkg as Record<string, unknown>).pyreonlint\n if (field && typeof field === 'object') return field as LintConfigFile\n return null\n}\n\n/**\n * Load a config file from a specific path.\n */\nexport function loadConfigFromPath(filePath: string): LintConfigFile | null {\n return tryReadJson(resolve(filePath))\n}\n\nfunction tryReadJson(filePath: string): any | null {\n if (!existsSync(filePath)) return null\n try {\n const raw = readFileSync(filePath, 'utf-8').trim()\n if (!raw) return null\n return JSON.parse(raw)\n } catch {\n return null\n }\n}\n","import type { ImportInfo } from '../types'\n\nexport type { ImportInfo }\n\n// ── Constants ───────────────────────────────────────────────────────────────\n\nexport const PYREON_PREFIX = '@pyreon/'\n\nexport const REACTIVITY_APIS = new Set([\n 'signal',\n 'computed',\n 'effect',\n 'batch',\n 'onCleanup',\n 'createSelector',\n 'createStore',\n 'untrack',\n])\n\nexport const LIFECYCLE_APIS = new Set(['onMount', 'onUnmount'])\n\nexport const CONTEXT_APIS = new Set(['createContext', 'provide', 'pushContext', 'popContext'])\n\nexport const JSX_COMPONENTS = new Set([\n 'For',\n 'Show',\n 'Switch',\n 'Match',\n 'Dynamic',\n 'ErrorBoundary',\n 'Suspense',\n 'Portal',\n])\n\nexport const HEAVY_PACKAGES = new Set([\n '@pyreon/charts',\n '@pyreon/code',\n '@pyreon/document',\n '@pyreon/flow',\n])\n\nexport const BROWSER_GLOBALS = new Set([\n 'window',\n 'document',\n 'navigator',\n 'location',\n 'history',\n 'localStorage',\n 'sessionStorage',\n 'indexedDB',\n 'fetch',\n 'XMLHttpRequest',\n 'WebSocket',\n 'requestAnimationFrame',\n 'cancelAnimationFrame',\n 'IntersectionObserver',\n 'MutationObserver',\n 'ResizeObserver',\n 'matchMedia',\n 'getComputedStyle',\n 'addEventListener',\n 'removeEventListener',\n])\n\n// ── Functions ───────────────────────────────────────────────────────────────\n\nexport function isPyreonImport(source: string): boolean {\n return source.startsWith(PYREON_PREFIX)\n}\n\nexport function isPyreonPackage(source: string): boolean {\n return source.startsWith(PYREON_PREFIX)\n}\n\nexport function extractImportInfo(node: any): ImportInfo | null {\n if (node.type !== 'ImportDeclaration') return null\n\n const source = node.source?.value as string\n if (!source) return null\n\n const specifiers: ImportInfo['specifiers'] = []\n let isDefault = false\n let isNamespace = false\n\n for (const spec of node.specifiers ?? []) {\n if (spec.type === 'ImportDefaultSpecifier') {\n isDefault = true\n specifiers.push({ imported: 'default', local: spec.local?.name ?? '' })\n } else if (spec.type === 'ImportNamespaceSpecifier') {\n isNamespace = true\n specifiers.push({ imported: '*', local: spec.local?.name ?? '' })\n } else if (spec.type === 'ImportSpecifier') {\n const imported =\n spec.imported?.type === 'Identifier' ? spec.imported.name : (spec.imported?.value ?? '')\n specifiers.push({ imported, local: spec.local?.name ?? '' })\n }\n }\n\n return { source, specifiers, isDefault, isNamespace }\n}\n\nexport function importsName(imports: ImportInfo[], name: string, fromPackage?: string): boolean {\n return imports.some(\n (imp) =>\n (!fromPackage || imp.source === fromPackage) &&\n imp.specifiers.some((s) => s.imported === name),\n )\n}\n\nexport function getLocalName(\n imports: ImportInfo[],\n name: string,\n fromPackage?: string,\n): string | null {\n for (const imp of imports) {\n if (fromPackage && imp.source !== fromPackage) continue\n for (const s of imp.specifiers) {\n if (s.imported === name) return s.local\n }\n }\n return null\n}\n","import type { Span } from '../types'\nimport { BROWSER_GLOBALS } from './imports'\n\n/** Check if a node is a call expression to a specific function name. */\nexport function isCallTo(node: any, name: string): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'Identifier' &&\n node.callee.name === name\n )\n}\n\n/** Check if a node is a call expression to any of the given function names. */\nexport function isCallToAny(node: any, names: Set<string>): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'Identifier' &&\n names.has(node.callee.name)\n )\n}\n\n/** Check if a node is a member call like `obj.method()`. */\nexport function isMemberCallTo(node: any, objectName: string, methodName: string): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'MemberExpression' &&\n node.callee.object?.type === 'Identifier' &&\n node.callee.object.name === objectName &&\n node.callee.property?.type === 'Identifier' &&\n node.callee.property.name === methodName\n )\n}\n\n/** Check if a node is a JSX element (opening or self-closing). */\nexport function isJSXElement(node: any): boolean {\n return node.type === 'JSXElement' || node.type === 'JSXFragment'\n}\n\n/** Get the tag name of a JSX element. */\nexport function getJSXTagName(node: any): string | null {\n if (node.type === 'JSXElement') {\n const opening = node.openingElement\n if (!opening) return null\n const name = opening.name\n if (name?.type === 'JSXIdentifier') return name.name\n if (name?.type === 'JSXMemberExpression') {\n return `${name.object?.name ?? ''}.${name.property?.name ?? ''}`\n }\n }\n return null\n}\n\n/** Get a JSX attribute by name from an opening element. */\nexport function getJSXAttribute(openingElement: any, attrName: string): any | null {\n const attrs = openingElement.attributes ?? []\n for (const attr of attrs) {\n if (\n attr.type === 'JSXAttribute' &&\n attr.name?.type === 'JSXIdentifier' &&\n attr.name.name === attrName\n ) {\n return attr\n }\n }\n return null\n}\n\n/** Check if a JSX opening element has an attribute. */\nexport function hasJSXAttribute(openingElement: any, attrName: string): boolean {\n return getJSXAttribute(openingElement, attrName) !== null\n}\n\n/** Check if a node is inside a function (arrow or regular). */\nexport function isInsideFunction(ancestors: any[]): boolean {\n return ancestors.some(\n (a) =>\n a.type === 'FunctionDeclaration' ||\n a.type === 'FunctionExpression' ||\n a.type === 'ArrowFunctionExpression',\n )\n}\n\n/** Check if a node is inside JSX. */\nexport function isInsideJSX(ancestors: any[]): boolean {\n return ancestors.some((a) => a.type === 'JSXElement' || a.type === 'JSXFragment')\n}\n\n/** Check if a node is an array .map() call. */\nexport function isArrayMapCall(node: any): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'MemberExpression' &&\n node.callee.property?.type === 'Identifier' &&\n node.callee.property.name === 'map'\n )\n}\n\n/** Check if a node is a function expression or arrow function. */\nexport function isFunction(node: any): boolean {\n return (\n node.type === 'FunctionDeclaration' ||\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n )\n}\n\n/** Check if a node is a destructuring pattern. */\nexport function isDestructuring(node: any): boolean {\n return node.type === 'ObjectPattern' || node.type === 'ArrayPattern'\n}\n\n/** Check if a node is a ternary with JSX in either branch. */\nexport function isTernaryWithJSX(node: any): boolean {\n if (node.type !== 'ConditionalExpression') return false\n return containsJSX(node.consequent) || containsJSX(node.alternate)\n}\n\n/** Check if a node contains JSX anywhere. */\nfunction containsJSX(node: any): boolean {\n if (!node) return false\n if (node.type === 'JSXElement' || node.type === 'JSXFragment') return true\n if (node.type === 'ParenthesizedExpression') return containsJSX(node.expression)\n return false\n}\n\n/** Check if a JSX element has JSX children. */\nexport function hasJSXChild(node: any): boolean {\n if (node.type !== 'JSXElement') return false\n return (node.children ?? []).some((c: any) => c.type === 'JSXElement' || c.type === 'JSXFragment')\n}\n\n/** Check if a node is a logical AND with JSX. */\nexport function isLogicalAndWithJSX(node: any): boolean {\n if (node.type !== 'LogicalExpression' || node.operator !== '&&') return false\n return containsJSX(node.right)\n}\n\n/** Check if a node is a .peek() call. */\nexport function isPeekCall(node: any): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'MemberExpression' &&\n node.callee.property?.type === 'Identifier' &&\n node.callee.property.name === 'peek'\n )\n}\n\n/** Check if a node is a .set() call. */\nexport function isSetCall(node: any): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'MemberExpression' &&\n node.callee.property?.type === 'Identifier' &&\n node.callee.property.name === 'set'\n )\n}\n\n/** Check if a node references a browser global. */\nexport function isBrowserGlobal(node: any): boolean {\n return node.type === 'Identifier' && BROWSER_GLOBALS.has(node.name)\n}\n\n/** Get the span (byte offsets) of a node. */\nexport function getSpan(node: any): Span {\n return { start: node.start as number, end: node.end as number }\n}\n\n/** Check if a node is inside a `if (__DEV__)` guard. */\nexport function isInsideDevGuard(ancestors: any[]): boolean {\n return ancestors.some(\n (a) => a.type === 'IfStatement' && a.test?.type === 'Identifier' && a.test.name === '__DEV__',\n )\n}\n\n/** Check if a node is inside an onMount callback. */\nexport function isInsideOnMount(ancestors: any[]): boolean {\n return ancestors.some(\n (a) =>\n a.type === 'CallExpression' && a.callee?.type === 'Identifier' && a.callee.name === 'onMount',\n )\n}\n\n/** Check if a node is inside a typeof guard (e.g., `typeof window !== \"undefined\"`). */\nexport function isInsideTypeofGuard(ancestors: any[]): boolean {\n return ancestors.some(\n (a) =>\n a.type === 'IfStatement' &&\n a.test?.type === 'BinaryExpression' &&\n a.test.left?.type === 'UnaryExpression' &&\n a.test.left.operator === 'typeof',\n )\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const dialogA11y: Rule = {\n meta: {\n id: 'pyreon/dialog-a11y',\n category: 'accessibility',\n description: 'Warn when <dialog> is missing aria-label or aria-labelledby.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'dialog') return\n\n const hasLabel = hasJSXAttribute(node, 'aria-label')\n const hasLabelledBy = hasJSXAttribute(node, 'aria-labelledby')\n\n if (!hasLabel && !hasLabelledBy) {\n context.report({\n message:\n '`<dialog>` missing `aria-label` or `aria-labelledby` — provide an accessible label for screen readers.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const overlayA11y: Rule = {\n meta: {\n id: 'pyreon/overlay-a11y',\n category: 'accessibility',\n description: 'Warn when <Overlay> is missing role, aria-label, or aria-labelledby.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'Overlay') return\n\n const hasRole = hasJSXAttribute(node, 'role')\n const hasLabel = hasJSXAttribute(node, 'aria-label')\n const hasLabelledBy = hasJSXAttribute(node, 'aria-labelledby')\n\n if (!hasRole && !hasLabel && !hasLabelledBy) {\n context.report({\n message:\n '`<Overlay>` missing `role`, `aria-label`, or `aria-labelledby` — provide accessibility attributes for screen readers.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const toastA11y: Rule = {\n meta: {\n id: 'pyreon/toast-a11y',\n category: 'accessibility',\n description: 'Warn when toast-like components are missing role or aria-live attributes.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier') return\n\n const tagName: string = name.name\n // Skip non-PascalCase and the Toaster container itself\n if (tagName === 'Toaster') return\n const firstChar = tagName[0]\n if (!firstChar || firstChar !== firstChar.toUpperCase()) return\n if (!tagName.toLowerCase().includes('toast')) return\n\n const hasRole = hasJSXAttribute(node, 'role')\n const hasAriaLive = hasJSXAttribute(node, 'aria-live')\n\n if (!hasRole && !hasAriaLive) {\n context.report({\n message: `Toast component \\`<${tagName}>\\` missing \\`role\\` or \\`aria-live\\` — add \\`role=\"alert\"\\` and \\`aria-live=\"polite\"\\` for screen reader accessibility.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const devGuardWarnings: Rule = {\n meta: {\n id: 'pyreon/dev-guard-warnings',\n category: 'architecture',\n description: 'Require console.warn/error calls to be wrapped in `if (__DEV__)` guards.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n const filePath = context.getFilePath()\n // Skip test and example files\n if (\n filePath.includes('/tests/') ||\n filePath.includes('/test/') ||\n filePath.includes('/examples/') ||\n filePath.includes('.test.') ||\n filePath.includes('.spec.')\n ) {\n return {}\n }\n\n let devGuardDepth = 0\n const callbacks: VisitorCallbacks = {\n IfStatement(node: any) {\n if (node.test?.type === 'Identifier' && node.test.name === '__DEV__') {\n devGuardDepth++\n }\n },\n 'IfStatement:exit'(node: any) {\n if (node.test?.type === 'Identifier' && node.test.name === '__DEV__') {\n devGuardDepth--\n }\n },\n CallExpression(node: any) {\n if (devGuardDepth > 0) return\n\n const callee = node.callee\n if (\n callee?.type === 'MemberExpression' &&\n callee.object?.type === 'Identifier' &&\n callee.object.name === 'console' &&\n callee.property?.type === 'Identifier' &&\n (callee.property.name === 'warn' || callee.property.name === 'error')\n ) {\n context.report({\n message: `\\`console.${callee.property.name}()\\` without \\`__DEV__\\` guard — dev warnings must be tree-shakeable in production. Wrap in \\`if (__DEV__) { ... }\\`.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { isPyreonImport } from '../../utils/imports'\n\nconst LAYER_ORDER: Record<string, number> = {\n '@pyreon/reactivity': 0,\n '@pyreon/core': 1,\n '@pyreon/compiler': 1,\n '@pyreon/runtime-dom': 2,\n '@pyreon/runtime-server': 2,\n '@pyreon/router': 3,\n '@pyreon/head': 4,\n '@pyreon/server': 5,\n}\n\nfunction getLayer(source: string): number | null {\n return LAYER_ORDER[source] ?? null\n}\n\nfunction getFileLayer(filePath: string): number | null {\n for (const [pkg, layer] of Object.entries(LAYER_ORDER)) {\n const pkgName = pkg.replace('@pyreon/', '')\n if (filePath.includes(`/packages/core/${pkgName}/`)) return layer\n }\n return null\n}\n\nexport const noCircularImport: Rule = {\n meta: {\n id: 'pyreon/no-circular-import',\n category: 'architecture',\n description: 'Enforce package layer order to prevent circular imports between core packages.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n const filePath = context.getFilePath()\n const fileLayer = getFileLayer(filePath)\n if (fileLayer === null) return {}\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const source = node.source?.value as string\n if (!source || !isPyreonImport(source)) return\n\n const importLayer = getLayer(source)\n if (importLayer === null) return\n\n if (importLayer >= fileLayer) {\n context.report({\n message: `Importing \\`${source}\\` (layer ${importLayer}) from layer ${fileLayer} — this violates the package layer order and may cause circular imports.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { isPyreonImport } from '../../utils/imports'\n\ntype PackageCategory = 'core' | 'fundamentals' | 'tools' | 'ui-system'\n\nconst CORE_PACKAGES = new Set([\n '@pyreon/reactivity',\n '@pyreon/core',\n '@pyreon/compiler',\n '@pyreon/runtime-dom',\n '@pyreon/runtime-server',\n '@pyreon/router',\n '@pyreon/head',\n '@pyreon/server',\n])\n\nconst UI_PACKAGES = new Set([\n '@pyreon/ui-core',\n '@pyreon/styler',\n '@pyreon/unistyle',\n '@pyreon/elements',\n '@pyreon/attrs',\n '@pyreon/rocketstyle',\n '@pyreon/coolgrid',\n '@pyreon/kinetic',\n '@pyreon/kinetic-presets',\n '@pyreon/connector-document',\n '@pyreon/document-primitives',\n])\n\nfunction getImportCategory(source: string): PackageCategory | null {\n if (CORE_PACKAGES.has(source)) return 'core'\n if (UI_PACKAGES.has(source)) return 'ui-system'\n return null\n}\n\nfunction getFileCategory(filePath: string): PackageCategory | null {\n if (filePath.includes('/packages/core/')) return 'core'\n if (filePath.includes('/packages/ui-system/')) return 'ui-system'\n if (filePath.includes('/packages/fundamentals/')) return 'fundamentals'\n if (filePath.includes('/packages/tools/')) return 'tools'\n return null\n}\n\nexport const noCrossLayerImport: Rule = {\n meta: {\n id: 'pyreon/no-cross-layer-import',\n category: 'architecture',\n description: 'Prevent core packages from importing ui-system packages.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n const filePath = context.getFilePath()\n const fileCategory = getFileCategory(filePath)\n if (fileCategory !== 'core') return {}\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const source = node.source?.value as string\n if (!source || !isPyreonImport(source)) return\n\n const importCategory = getImportCategory(source)\n if (importCategory === 'ui-system') {\n context.report({\n message: `Core package importing ui-system package \\`${source}\\` — core packages must not depend on ui-system.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { isPyreonImport } from '../../utils/imports'\n\nconst DEEP_IMPORT_PATTERN = /@pyreon\\/[^/]+\\/(src|dist|lib)\\//\n\nexport const noDeepImport: Rule = {\n meta: {\n id: 'pyreon/no-deep-import',\n category: 'architecture',\n description:\n 'Disallow importing from @pyreon/*/src/, /dist/, or /lib/ — use public exports instead.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const source = node.source?.value as string\n if (!source || !isPyreonImport(source)) return\n\n if (DEEP_IMPORT_PATTERN.test(source)) {\n context.report({\n message: `Deep import \\`${source}\\` — import from the package's public exports instead.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noErrorWithoutPrefix: Rule = {\n meta: {\n id: 'pyreon/no-error-without-prefix',\n category: 'architecture',\n description: 'Require error messages to be prefixed with [Pyreon].',\n severity: 'warn',\n fixable: true,\n },\n create(context) {\n const filePath = context.getFilePath()\n // Skip test files\n if (\n filePath.includes('/tests/') ||\n filePath.includes('/test/') ||\n filePath.includes('.test.') ||\n filePath.includes('.spec.')\n ) {\n return {}\n }\n\n const callbacks: VisitorCallbacks = {\n ThrowStatement(node: any) {\n const arg = node.argument\n if (!arg || arg.type !== 'NewExpression') return\n const callee = arg.callee\n if (!callee || callee.type !== 'Identifier' || callee.name !== 'Error') return\n\n const args = arg.arguments\n if (!args || args.length === 0) return\n\n const firstArg = args[0]\n if (!firstArg) return\n\n if (firstArg.type === 'Literal' || firstArg.type === 'StringLiteral') {\n const value = firstArg.value as string\n if (typeof value === 'string' && !value.startsWith('[Pyreon]')) {\n const argSpan = getSpan(firstArg)\n // Fix: add [Pyreon] prefix\n const quote = context.getSourceText()[argSpan.start]\n const fixedValue = `${quote}[Pyreon] ${value}${quote}`\n context.report({\n message:\n 'Error message missing `[Pyreon]` prefix — all framework errors should be prefixed for identification.',\n span: getSpan(node),\n fix: { span: argSpan, replacement: fixedValue },\n })\n }\n }\n\n if (firstArg.type === 'TemplateLiteral') {\n const quasis = firstArg.quasis\n if (quasis && quasis.length > 0) {\n const first = quasis[0]\n const raw = first.value?.raw ?? first.value?.cooked ?? ''\n if (!raw.startsWith('[Pyreon]')) {\n const argSpan = getSpan(firstArg)\n const source = context.getSourceText().slice(argSpan.start, argSpan.end)\n const fixed = source.replace(/^`/, '`[Pyreon] ')\n context.report({\n message:\n 'Error message missing `[Pyreon]` prefix — all framework errors should be prefixed for identification.',\n span: getSpan(node),\n fix: { span: argSpan, replacement: fixed },\n })\n }\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\n/**\n * `pyreon/no-process-dev-gate` — flag the broken `typeof process` dev-mode gate\n * pattern that is dead code in real Vite browser bundles.\n *\n * The pattern this rule catches:\n *\n * ```ts\n * const __DEV__ = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'\n * ```\n *\n * This works in vitest (Node, `process` is defined) but is **silently dead\n * code in real Vite browser bundles** because Vite does not polyfill\n * `process` for the client. Every dev warning gated on this constant never\n * fires for real users in dev mode.\n *\n * The fix is to use `import.meta.env.DEV`, which Vite/Rolldown literal-replace\n * at build time:\n *\n * ```ts\n * // No const needed — read directly at the use site so the bundler can fold:\n * if (!import.meta.env?.DEV) return\n * ```\n *\n * Vitest sets `import.meta.env.DEV === true` automatically (because it is\n * Vite-based), so existing tests continue to pass.\n *\n * Reference implementation: `packages/fundamentals/flow/src/layout.ts:warnIgnoredOptions`.\n *\n * **Auto-fix**: replaces the assignment with `import.meta.env?.DEV === true`.\n * Does NOT delete the const declaration — that has to happen by hand because\n * the variable name and downstream usages may need updating in callers.\n *\n * **Server-package exception**: server-only files run in Node where `process`\n * is always defined, so the pattern is correct there. The rule skips files\n * matching `packages/zero/`, `packages/core/server/`, `packages/core/runtime-server/`,\n * `packages/tools/vite-plugin/`, and any file containing `/server/` in its\n * path. Add new server packages to `SERVER_PACKAGE_PATTERNS` below.\n */\n/**\n * File-path patterns for server-only packages. Substring match against the\n * file path passed to the rule. Patterns intentionally do NOT start with `/`\n * so they match both absolute paths (`/Users/.../packages/zero/...`) and\n * relative paths (`packages/zero/...`) — different lint runners pass paths\n * differently.\n */\nconst SERVER_PACKAGE_PATTERNS = [\n 'packages/zero/',\n 'packages/core/server/',\n 'packages/core/runtime-server/',\n 'packages/tools/vite-plugin/',\n 'packages/tools/cli/',\n 'packages/tools/lint/',\n 'packages/tools/mcp/',\n 'packages/tools/storybook/',\n 'packages/tools/typescript/',\n 'scripts/',\n]\n\nfunction isServerOnlyFile(filePath: string): boolean {\n return SERVER_PACKAGE_PATTERNS.some((pat) => filePath.includes(pat))\n}\n\nexport const noProcessDevGate: Rule = {\n meta: {\n id: 'pyreon/no-process-dev-gate',\n category: 'architecture',\n description:\n 'Forbid `typeof process !== \"undefined\" && process.env.NODE_ENV !== \"production\"` as a dev-mode gate. Use `import.meta.env.DEV` instead — `typeof process` is dead code in real Vite browser bundles because Vite does not polyfill `process` for the client.',\n severity: 'error',\n fixable: true,\n },\n create(context) {\n const filePath = context.getFilePath()\n\n // Skip test files — vitest has `process`, the gate works there, and\n // tests are not shipped to users.\n if (\n filePath.includes('/tests/') ||\n filePath.includes('/test/') ||\n filePath.includes('/__tests__/') ||\n filePath.includes('.test.') ||\n filePath.includes('.spec.')\n ) {\n return {}\n }\n\n // Skip server-only packages — they run in Node where `process` is\n // always defined, so the pattern is correct there.\n if (isServerOnlyFile(filePath)) {\n return {}\n }\n\n /**\n * Match the broken pattern at the AST level. We're looking for any\n * `LogicalExpression` whose two sides are:\n *\n * 1. `typeof process !== 'undefined'` (a UnaryExpression on the LHS\n * of a BinaryExpression with operator `!==`)\n * 2. `process.env.NODE_ENV !== 'production'` (a MemberExpression on\n * the LHS of a BinaryExpression with operator `!==`)\n *\n * The order can be either way (process check first or NODE_ENV check\n * first), and the operator can be `&&` or `||` (we only flag `&&`\n * because `||` doesn't make sense as a dev gate).\n */\n function isTypeofProcessCheck(node: any): boolean {\n // typeof process !== 'undefined'\n if (node?.type !== 'BinaryExpression') return false\n if (node.operator !== '!==' && node.operator !== '!=') return false\n const left = node.left\n const right = node.right\n if (left?.type !== 'UnaryExpression' || left.operator !== 'typeof') return false\n if (left.argument?.type !== 'Identifier' || left.argument.name !== 'process') return false\n if (\n (right?.type === 'Literal' || right?.type === 'StringLiteral') &&\n right.value === 'undefined'\n ) {\n return true\n }\n return false\n }\n\n function isNodeEnvCheck(node: any): boolean {\n // process.env.NODE_ENV !== 'production'\n if (node?.type !== 'BinaryExpression') return false\n if (node.operator !== '!==' && node.operator !== '!=') return false\n const left = node.left\n const right = node.right\n if (left?.type !== 'MemberExpression') return false\n if (left.object?.type !== 'MemberExpression') return false\n if (left.object.object?.type !== 'Identifier' || left.object.object.name !== 'process') {\n return false\n }\n if (left.object.property?.type !== 'Identifier' || left.object.property.name !== 'env') {\n return false\n }\n if (left.property?.type !== 'Identifier' || left.property.name !== 'NODE_ENV') return false\n if (\n (right?.type === 'Literal' || right?.type === 'StringLiteral') &&\n right.value === 'production'\n ) {\n return true\n }\n return false\n }\n\n function isBrokenDevGate(node: any): boolean {\n if (node?.type !== 'LogicalExpression') return false\n if (node.operator !== '&&') return false\n // Order can be (typeof process) && (NODE_ENV) OR vice versa\n return (\n (isTypeofProcessCheck(node.left) && isNodeEnvCheck(node.right)) ||\n (isNodeEnvCheck(node.left) && isTypeofProcessCheck(node.right))\n )\n }\n\n const callbacks: VisitorCallbacks = {\n LogicalExpression(node: any) {\n if (!isBrokenDevGate(node)) return\n\n const span = getSpan(node)\n // Auto-fix: replace the entire `typeof process ... && process.env.NODE_ENV ...`\n // expression with `import.meta.env?.DEV === true`. We use optional\n // chaining + strict equality so the expression is `false` (not\n // `undefined`) when `import.meta.env` is missing — preserving the\n // boolean shape callers expect.\n const replacement = 'import.meta.env?.DEV === true'\n\n context.report({\n message:\n '`typeof process !== \"undefined\" && process.env.NODE_ENV !== \"production\"` is dead code in real Vite browser bundles — Vite does not polyfill `process`, so this guard is `false` and any wrapped dev warnings never fire for real users. Use `import.meta.env.DEV` instead, which Vite literal-replaces at build time and tree-shakes correctly in prod. Reference implementation: `packages/fundamentals/flow/src/layout.ts:warnIgnoredOptions`.',\n span,\n fix: { span, replacement },\n })\n },\n }\n\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noSubmitWithoutValidation: Rule = {\n meta: {\n id: 'pyreon/no-submit-without-validation',\n category: 'form',\n description: 'Warn when useForm() has onSubmit but no validators or schema.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'useForm')) return\n const args = node.arguments\n if (!args || args.length === 0) return\n\n const options = args[0]\n if (!options || options.type !== 'ObjectExpression') return\n\n let hasOnSubmit = false\n let hasValidation = false\n\n for (const prop of options.properties ?? []) {\n if (prop.type !== 'Property') continue\n const key = prop.key\n if (!key) continue\n const name = key.type === 'Identifier' ? key.name : null\n if (name === 'onSubmit') hasOnSubmit = true\n if (name === 'validators' || name === 'schema') hasValidation = true\n }\n\n if (hasOnSubmit && !hasValidation) {\n context.report({\n message:\n '`useForm()` has `onSubmit` without `validators` or `schema` — consider adding validation for data integrity.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noUnregisteredField: Rule = {\n meta: {\n id: 'pyreon/no-unregistered-field',\n category: 'form',\n description: 'Warn when useField() is called without a corresponding register() call.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const fieldDecls = new Map<string, { span: { start: number; end: number } }>()\n const registeredNames = new Set<string>()\n\n const callbacks: VisitorCallbacks = {\n VariableDeclarator(node: any) {\n const init = node.init\n if (!init || !isCallTo(init, 'useField')) return\n const id = node.id\n if (!id || id.type !== 'Identifier') return\n fieldDecls.set(id.name, { span: getSpan(node) })\n },\n CallExpression(node: any) {\n const callee = node.callee\n if (!callee || callee.type !== 'MemberExpression') return\n if (callee.property?.type !== 'Identifier' || callee.property.name !== 'register') return\n if (callee.object?.type === 'Identifier') {\n registeredNames.add(callee.object.name)\n }\n },\n 'Program:exit'() {\n for (const [name, { span }] of fieldDecls) {\n if (!registeredNames.has(name)) {\n context.report({\n message: `\\`useField()\\` result \\`${name}\\` is never registered — call \\`${name}.register()\\` to connect it to the form.`,\n span,\n })\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\nimport { extractImportInfo } from '../../utils/imports'\n\nexport const preferFieldArray: Rule = {\n meta: {\n id: 'pyreon/prefer-field-array',\n category: 'form',\n description: 'Suggest useFieldArray() instead of signal([]) in files that import @pyreon/form.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n let importsForm = false\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (info && info.source === '@pyreon/form') {\n importsForm = true\n }\n },\n CallExpression(node: any) {\n if (!importsForm) return\n if (!isCallTo(node, 'signal')) return\n\n const args = node.arguments\n if (!args || args.length === 0) return\n const firstArg = args[0]\n if (firstArg?.type === 'ArrayExpression') {\n context.report({\n message:\n '`signal([])` in a form file — consider using `useFieldArray()` for dynamic array fields with stable keys.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noRawAddEventListener: Rule = {\n meta: {\n id: 'pyreon/no-raw-addeventlistener',\n category: 'hooks',\n description: 'Suggest useEventListener() instead of raw .addEventListener() calls.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n const callee = node.callee\n if (!callee || callee.type !== 'MemberExpression') return\n if (callee.property?.type !== 'Identifier' || callee.property.name !== 'addEventListener')\n return\n context.report({\n message:\n 'Raw `.addEventListener()` — consider using `useEventListener()` from `@pyreon/hooks` for auto-cleanup on unmount.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nconst STORAGE_OBJECTS = new Set(['localStorage', 'sessionStorage'])\nconst STORAGE_METHODS = new Set(['getItem', 'setItem', 'removeItem'])\n\nexport const noRawLocalStorage: Rule = {\n meta: {\n id: 'pyreon/no-raw-localstorage',\n category: 'hooks',\n description: 'Suggest useStorage() instead of raw localStorage/sessionStorage access.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n const callee = node.callee\n if (!callee || callee.type !== 'MemberExpression') return\n if (\n callee.object?.type === 'Identifier' &&\n STORAGE_OBJECTS.has(callee.object.name) &&\n callee.property?.type === 'Identifier' &&\n STORAGE_METHODS.has(callee.property.name)\n ) {\n context.report({\n message: `Raw \\`${callee.object.name}.${callee.property.name}()\\` — consider using \\`useStorage()\\` from \\`@pyreon/storage\\` for reactive, cross-tab synced storage.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nconst TIMER_FNS = new Set(['setInterval', 'setTimeout'])\n\nexport const noRawSetInterval: Rule = {\n meta: {\n id: 'pyreon/no-raw-setinterval',\n category: 'hooks',\n description: 'Suggest wrapping setInterval/setTimeout in onMount for automatic cleanup.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n let mountDepth = 0\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (isCallTo(node, 'onMount')) {\n mountDepth++\n }\n\n if (mountDepth > 0) return\n\n const callee = node.callee\n if (!callee || callee.type !== 'Identifier') return\n if (TIMER_FNS.has(callee.name)) {\n context.report({\n message: `\\`${callee.name}()\\` outside \\`onMount\\` — wrap in \\`onMount(() => { ... return () => clear... })\\` for automatic cleanup.`,\n span: getSpan(node),\n })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'onMount')) {\n mountDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isLogicalAndWithJSX } from '../../utils/ast'\n\nexport const noAndConditional: Rule = {\n meta: {\n id: 'pyreon/no-and-conditional',\n category: 'jsx',\n description: 'Prefer <Show> over `&&` with JSX in expression containers.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let jsxExpressionDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXExpressionContainer() {\n jsxExpressionDepth++\n },\n 'JSXExpressionContainer:exit'() {\n jsxExpressionDepth--\n },\n LogicalExpression(node: any) {\n if (jsxExpressionDepth === 0) return\n if (!isLogicalAndWithJSX(node)) return\n context.report({\n message: '`&&` with JSX — use `<Show>` for conditional rendering.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { extractImportInfo, type ImportInfo } from '../../utils/imports'\n\nexport const noChildrenAccess: Rule = {\n meta: {\n id: 'pyreon/no-children-access',\n category: 'jsx',\n description: 'Inform about direct props.children access in renderer files.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const imports: ImportInfo[] = []\n let isRendererFile = false\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (info) {\n imports.push(info)\n if (info.source === '@pyreon/runtime-server' || info.source === '@pyreon/runtime-dom') {\n isRendererFile = true\n }\n }\n },\n MemberExpression(node: any) {\n if (!isRendererFile) return\n if (\n node.object?.type === 'Identifier' &&\n node.property?.type === 'Identifier' &&\n node.property.name === 'children'\n ) {\n context.report({\n message:\n 'Direct `props.children` access in a renderer file — children are already merged via `mergeChildrenIntoProps`.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noClassName: Rule = {\n meta: {\n id: 'pyreon/no-classname',\n category: 'jsx',\n description: 'Use `class` instead of `className` — Pyreon uses standard HTML attributes.',\n severity: 'error',\n fixable: true,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXAttribute(node: any) {\n if (node.name?.type !== 'JSXIdentifier') return\n if (node.name.name !== 'className') return\n const nameSpan = getSpan(node.name)\n context.report({\n message: 'Use `class` instead of `className` — Pyreon uses standard HTML attributes.',\n span: getSpan(node),\n fix: { span: nameSpan, replacement: 'class' },\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noHtmlFor: Rule = {\n meta: {\n id: 'pyreon/no-htmlfor',\n category: 'jsx',\n description: 'Use `for` instead of `htmlFor` — Pyreon uses standard HTML attributes.',\n severity: 'error',\n fixable: true,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXAttribute(node: any) {\n if (node.name?.type !== 'JSXIdentifier') return\n if (node.name.name !== 'htmlFor') return\n const nameSpan = getSpan(node.name)\n context.report({\n message: 'Use `for` instead of `htmlFor` — Pyreon uses standard HTML attributes.',\n span: getSpan(node),\n fix: { span: nameSpan, replacement: 'for' },\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getJSXAttribute, getSpan } from '../../utils/ast'\n\nexport const noIndexAsBy: Rule = {\n meta: {\n id: 'pyreon/no-index-as-by',\n category: 'jsx',\n description: 'Disallow using index as `by` prop on <For> — use a unique key instead.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'For') return\n\n const byAttr = getJSXAttribute(node, 'by')\n if (!byAttr) return\n\n const value = byAttr.value\n if (!value || value.type !== 'JSXExpressionContainer') return\n\n const expr = value.expression\n if (!expr) return\n\n // Detect: by={(_, i) => i} or by={(item, index) => index}\n if (expr.type === 'ArrowFunctionExpression' || expr.type === 'FunctionExpression') {\n const params = expr.params\n if (!params || params.length < 2) return\n\n const secondParam = params[1]\n if (!secondParam || secondParam.type !== 'Identifier') return\n\n const indexName = secondParam.name\n const body = expr.body\n\n // Arrow expression body: (_, i) => i\n if (body?.type === 'Identifier' && body.name === indexName) {\n context.report({\n message:\n 'Using index as `by` prop on `<For>` — use a unique key from the data instead.',\n span: getSpan(byAttr),\n })\n }\n\n // Block body: (_, i) => { return i }\n if (body?.type === 'BlockStatement') {\n const stmts = body.body\n if (stmts?.length === 1) {\n const stmt = stmts[0]\n if (\n stmt.type === 'ReturnStatement' &&\n stmt.argument?.type === 'Identifier' &&\n stmt.argument.name === indexName\n ) {\n context.report({\n message:\n 'Using index as `by` prop on `<For>` — use a unique key from the data instead.',\n span: getSpan(byAttr),\n })\n }\n }\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isArrayMapCall } from '../../utils/ast'\n\nexport const noMapInJsx: Rule = {\n meta: {\n id: 'pyreon/no-map-in-jsx',\n category: 'jsx',\n description: 'Prefer <For> over .map() inside JSX for reactive list rendering.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let jsxDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXElement() {\n jsxDepth++\n },\n 'JSXElement:exit'() {\n jsxDepth--\n },\n JSXFragment() {\n jsxDepth++\n },\n 'JSXFragment:exit'() {\n jsxDepth--\n },\n CallExpression(node: any) {\n if (jsxDepth === 0) return\n if (!isArrayMapCall(node)) return\n // Check callback contains JSX\n const args = node.arguments\n if (!args || args.length === 0) return\n const callback = args[0]\n if (!callback) return\n context.report({\n message: '`.map()` in JSX — use `<For>` for reactive list rendering instead.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const noMissingForBy: Rule = {\n meta: {\n id: 'pyreon/no-missing-for-by',\n category: 'jsx',\n description: 'Warn when <For> is used without a `by` prop.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'For') return\n if (hasJSXAttribute(node, 'by')) return\n context.report({\n message:\n '`<For>` without `by` prop — provide a key function for efficient reconciliation.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nconst INPUT_TAGS = new Set(['input', 'textarea', 'select'])\n\nexport const noOnChange: Rule = {\n meta: {\n id: 'pyreon/no-onchange',\n category: 'jsx',\n description:\n 'Prefer `onInput` over `onChange` on input elements for keypress-by-keypress updates.',\n severity: 'warn',\n fixable: true,\n },\n create(context) {\n let currentTag: string | null = null\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (name?.type === 'JSXIdentifier' && INPUT_TAGS.has(name.name)) {\n currentTag = name.name\n } else {\n currentTag = null\n }\n\n if (!currentTag) return\n const attrs = node.attributes ?? []\n for (const attr of attrs) {\n if (\n attr.type === 'JSXAttribute' &&\n attr.name?.type === 'JSXIdentifier' &&\n attr.name.name === 'onChange'\n ) {\n const nameSpan = getSpan(attr.name)\n context.report({\n message: `Use \\`onInput\\` instead of \\`onChange\\` on \\`<${currentTag}>\\` for keypress-by-keypress updates.`,\n span: getSpan(attr),\n fix: { span: nameSpan, replacement: 'onInput' },\n })\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isDestructuring } from '../../utils/ast'\n\nfunction containsJSXReturn(node: any): boolean {\n if (!node) return false\n if (node.type === 'JSXElement' || node.type === 'JSXFragment') return true\n if (node.type === 'ParenthesizedExpression') return containsJSXReturn(node.expression)\n\n if (node.type === 'BlockStatement') {\n for (const stmt of node.body ?? []) {\n if (stmt.type === 'ReturnStatement' && containsJSXReturn(stmt.argument)) {\n return true\n }\n }\n }\n return false\n}\n\n/**\n * Extract destructured property names from an ObjectPattern.\n * Returns names for the fix suggestion.\n */\nfunction getDestructuredNames(pattern: any): string[] {\n if (pattern.type !== 'ObjectPattern') return []\n const names: string[] = []\n for (const prop of pattern.properties ?? []) {\n if (prop.type === 'ObjectProperty' && prop.key?.type === 'Identifier') {\n names.push(prop.key.name)\n }\n }\n return names\n}\n\nexport const noPropsDestructure: Rule = {\n meta: {\n id: 'pyreon/no-props-destructure',\n category: 'jsx',\n description:\n 'Disallow destructuring props in component functions — breaks reactive prop tracking. Use props.x or splitProps().',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n let functionDepth = 0\n\n const callbacks: VisitorCallbacks = {\n ArrowFunctionExpression(node: any) {\n functionDepth++\n checkFunction(node, context, functionDepth)\n },\n 'ArrowFunctionExpression:exit'() {\n functionDepth--\n },\n FunctionDeclaration(node: any) {\n functionDepth++\n checkFunction(node, context, functionDepth)\n },\n 'FunctionDeclaration:exit'() {\n functionDepth--\n },\n FunctionExpression(node: any) {\n functionDepth++\n checkFunction(node, context, functionDepth)\n },\n 'FunctionExpression:exit'() {\n functionDepth--\n },\n }\n return callbacks\n },\n}\n\nfunction checkFunction(node: any, context: any, depth: number) {\n const params = node.params\n if (!params || params.length === 0) return\n\n const firstParam = params[0]\n if (!isDestructuring(firstParam)) return\n\n // Skip HOC inner functions (depth > 1)\n if (depth > 1) return\n\n // Skip functions passed as arguments to HOC factories\n // e.g. createLink(({ href, ...rest }) => <a {...rest} />)\n const parent = node.parent\n if (parent?.type === 'CallExpression' && parent.arguments?.includes(node)) return\n\n const body = node.body\n if (!body) return\n\n if (containsJSXReturn(body)) {\n const names = getDestructuredNames(firstParam)\n const hasRest = (firstParam.properties ?? []).some((p: any) => p.type === 'RestElement')\n\n let suggestion = 'Use `props.x` pattern for reactive prop access.'\n if (names.length > 0) {\n const propsAccess = names.map((n) => `props.${n}`).join(', ')\n suggestion = `Use \\`props\\` parameter and access as ${propsAccess}.`\n if (hasRest) {\n suggestion += ` For rest props, use \\`splitProps(props, [${names.map((n) => `'${n}'`).join(', ')}])\\`.`\n }\n }\n\n context.report({\n message:\n `Destructured props in component function — breaks reactive prop tracking. ${suggestion}`,\n span: getSpan(firstParam),\n })\n }\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isTernaryWithJSX } from '../../utils/ast'\n\nexport const noTernaryConditional: Rule = {\n meta: {\n id: 'pyreon/no-ternary-conditional',\n category: 'jsx',\n description: 'Prefer <Show> over ternary expressions with JSX branches.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let jsxExpressionDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXExpressionContainer() {\n jsxExpressionDepth++\n },\n 'JSXExpressionContainer:exit'() {\n jsxExpressionDepth--\n },\n ConditionalExpression(node: any) {\n if (jsxExpressionDepth === 0) return\n if (!isTernaryWithJSX(node)) return\n context.report({\n message: 'Ternary with JSX — use `<Show>` for more efficient conditional rendering.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getJSXAttribute, getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const useByNotKey: Rule = {\n meta: {\n id: 'pyreon/use-by-not-key',\n category: 'jsx',\n description:\n 'Use `by` prop on <For> instead of `key` — JSX reserves `key` for VNode reconciliation.',\n severity: 'error',\n fixable: true,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const tagName = node.name?.type === 'JSXIdentifier' ? node.name.name : null\n if (tagName !== 'For') return\n const keyAttr = getJSXAttribute(node, 'key')\n if (!keyAttr) return\n if (hasJSXAttribute(node, 'by')) return // already has by\n\n const attrSpan = getSpan(keyAttr.name)\n context.report({\n message:\n 'Use `by` prop on `<For>` instead of `key` — JSX reserves `key` for VNode reconciliation.',\n span: getSpan(keyAttr),\n fix: { span: attrSpan, replacement: 'by' },\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nconst DOM_METHODS = new Set([\n 'querySelector',\n 'querySelectorAll',\n 'getElementById',\n 'getElementsByClassName',\n 'getElementsByTagName',\n])\n\nexport const noDomInSetup: Rule = {\n meta: {\n id: 'pyreon/no-dom-in-setup',\n category: 'lifecycle',\n description: 'Warn when DOM query methods are used outside onMount or effect.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let safeDepth = 0 // inside onMount or effect\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (isCallTo(node, 'onMount') || isCallTo(node, 'effect')) {\n safeDepth++\n }\n\n if (safeDepth > 0) return\n\n // Check for document.querySelector() etc.\n const callee = node.callee\n if (\n callee?.type === 'MemberExpression' &&\n callee.object?.type === 'Identifier' &&\n callee.object.name === 'document' &&\n callee.property?.type === 'Identifier' &&\n DOM_METHODS.has(callee.property.name)\n ) {\n context.report({\n message: `\\`document.${callee.property.name}()\\` outside \\`onMount\\`/\\`effect\\` — DOM is not available during SSR or setup phase.`,\n span: getSpan(node),\n })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'onMount') || isCallTo(node, 'effect')) {\n safeDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noEffectInMount: Rule = {\n meta: {\n id: 'pyreon/no-effect-in-mount',\n category: 'lifecycle',\n description:\n 'Inform when effect() is created inside onMount — effects are typically created at setup time.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n let mountDepth = 0\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (isCallTo(node, 'onMount')) {\n mountDepth++\n }\n if (mountDepth > 0 && isCallTo(node, 'effect')) {\n context.report({\n message:\n '`effect()` inside `onMount` — effects are typically created at component setup time, not inside lifecycle hooks.',\n span: getSpan(node),\n })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'onMount')) {\n mountDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nconst NEEDS_CLEANUP = new Set(['setInterval', 'addEventListener'])\n\nexport const noMissingCleanup: Rule = {\n meta: {\n id: 'pyreon/no-missing-cleanup',\n category: 'lifecycle',\n description:\n 'Warn when onMount uses setInterval/addEventListener without returning a cleanup function.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'onMount')) return\n const args = node.arguments\n if (!args || args.length === 0) return\n\n const fn = args[0]\n if (!fn) return\n if (fn.type !== 'ArrowFunctionExpression' && fn.type !== 'FunctionExpression') return\n\n const body = fn.body\n if (!body) return\n\n // Only check block bodies\n if (body.type !== 'BlockStatement') return\n\n let hasCleanupTarget = false\n let hasReturn = false\n\n function walk(n: any) {\n if (!n) return\n if (n.type === 'CallExpression') {\n const callee = n.callee\n if (callee?.type === 'Identifier' && NEEDS_CLEANUP.has(callee.name)) {\n hasCleanupTarget = true\n }\n if (\n callee?.type === 'MemberExpression' &&\n callee.property?.type === 'Identifier' &&\n NEEDS_CLEANUP.has(callee.property.name)\n ) {\n hasCleanupTarget = true\n }\n }\n if (n.type === 'ReturnStatement' && n.argument) {\n hasReturn = true\n }\n for (const key of Object.keys(n)) {\n const child = n[key]\n if (child && typeof child === 'object') {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item.type === 'string') walk(item)\n }\n } else if (typeof child.type === 'string') {\n walk(child)\n }\n }\n }\n }\n\n walk(body)\n\n if (hasCleanupTarget && !hasReturn) {\n context.report({\n message:\n '`onMount` uses `setInterval`/`addEventListener` without returning a cleanup function — this will cause a memory leak.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noMountInEffect: Rule = {\n meta: {\n id: 'pyreon/no-mount-in-effect',\n category: 'lifecycle',\n description: 'Warn when onMount is called inside effect().',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let effectDepth = 0\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (isCallTo(node, 'effect')) {\n effectDepth++\n }\n if (effectDepth > 0 && isCallTo(node, 'onMount')) {\n context.report({\n message:\n '`onMount` inside `effect()` — `onMount` runs once on mount, not on every effect re-run.',\n span: getSpan(node),\n })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'effect')) {\n effectDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { HEAVY_PACKAGES } from '../../utils/imports'\n\nexport const noEagerImport: Rule = {\n meta: {\n id: 'pyreon/no-eager-import',\n category: 'performance',\n description: 'Suggest lazy-loading heavy Pyreon packages (charts, code, document, flow).',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const source = node.source?.value as string\n if (!source) return\n if (HEAVY_PACKAGES.has(source)) {\n context.report({\n message: `Static import of \\`${source}\\` — consider using \\`lazy()\\` or dynamic \\`import()\\` to reduce initial bundle size.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noEffectInFor: Rule = {\n meta: {\n id: 'pyreon/no-effect-in-for',\n category: 'performance',\n description:\n 'Warn when effect() is created inside <For> — creates effects per item on every reconciliation.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let forJsxDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (name?.type === 'JSXIdentifier' && name.name === 'For') {\n forJsxDepth++\n }\n },\n JSXClosingElement(node: any) {\n const name = node.name\n if (name?.type === 'JSXIdentifier' && name.name === 'For') {\n forJsxDepth--\n }\n },\n CallExpression(node: any) {\n if (forJsxDepth === 0) return\n if (isCallTo(node, 'effect')) {\n context.report({\n message:\n '`effect()` inside `<For>` — this creates a new effect for every item on each reconciliation. Lift the effect outside.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const noLargeForWithoutBy: Rule = {\n meta: {\n id: 'pyreon/no-large-for-without-by',\n category: 'performance',\n description:\n 'Error when <For> is used without a `by` prop — critical for reconciliation performance.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'For') return\n if (hasJSXAttribute(node, 'by')) return\n context.report({\n message:\n '`<For>` without `by` prop — provide a key function for efficient reconciliation.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const preferShowOverDisplay: Rule = {\n meta: {\n id: 'pyreon/prefer-show-over-display',\n category: 'performance',\n description: 'Suggest <Show> over conditional `display` style property in JSX.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXAttribute(node: any) {\n if (node.name?.type !== 'JSXIdentifier' || node.name.name !== 'style') return\n const value = node.value\n if (!value || value.type !== 'JSXExpressionContainer') return\n const expr = value.expression\n if (!expr || expr.type !== 'ObjectExpression') return\n\n for (const prop of expr.properties ?? []) {\n if (prop.type !== 'Property') continue\n const key = prop.key\n if (!key) continue\n const propName =\n key.type === 'Identifier' ? key.name : key.type === 'Literal' ? key.value : null\n if (propName === 'display') {\n // Check if the value is conditional\n const val = prop.value\n if (\n val?.type === 'ConditionalExpression' ||\n val?.type === 'LogicalExpression' ||\n val?.type === 'CallExpression'\n ) {\n context.report({\n message:\n 'Conditional `display` style — consider using `<Show>` for conditional rendering instead of toggling CSS display.',\n span: getSpan(prop),\n })\n }\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nconst SKIP_PREFIXES = /^(use|get|is|has|[A-Z])/\n\nexport const noBareSignalInJsx: Rule = {\n meta: {\n id: 'pyreon/no-bare-signal-in-jsx',\n category: 'reactivity',\n description:\n 'Disallow bare signal calls in JSX text positions. Wrap in `() =>` for reactivity.',\n severity: 'error',\n fixable: true,\n },\n create(context) {\n let jsxDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXElement() {\n jsxDepth++\n },\n 'JSXElement:exit'() {\n jsxDepth--\n },\n JSXFragment() {\n jsxDepth++\n },\n 'JSXFragment:exit'() {\n jsxDepth--\n },\n JSXExpressionContainer(node: any) {\n if (jsxDepth === 0) return\n const expr = node.expression\n if (!expr || expr.type !== 'CallExpression') return\n const callee = expr.callee\n if (!callee || callee.type !== 'Identifier') return\n\n const name: string = callee.name\n if (SKIP_PREFIXES.test(name)) return\n\n const span = getSpan(node)\n const source = context.getSourceText()\n const original = source.slice(span.start, span.end)\n // {count()} → {() => count()}\n const inner = original.slice(1, -1) // strip { }\n const fixed = `{() => ${inner}}`\n\n context.report({\n message: `Bare signal call \\`${name}()\\` in JSX text — wrap in \\`() => ${name}()\\` for fine-grained reactivity.`,\n span,\n fix: { span, replacement: fixed },\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\n/**\n * Detects destructuring the return value of useContext().\n *\n * `const { mode } = useContext(ctx)` loses reactivity when the context\n * provides getter properties. The value is captured once at setup time.\n *\n * Correct: `const ctx = useContext(Ctx)` then read `ctx.mode` lazily.\n */\nexport const noContextDestructure: Rule = {\n meta: {\n id: 'pyreon/no-context-destructure',\n category: 'reactivity',\n description:\n 'Disallow destructuring useContext() — it breaks reactivity when context provides getters.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n VariableDeclarator(node: any) {\n // Match: const { x } = useContext(...)\n const id = node.id\n const init = node.init\n if (!id || !init) return\n if (id.type !== 'ObjectPattern') return\n if (\n init.type !== 'CallExpression' ||\n init.callee?.type !== 'Identifier' ||\n init.callee.name !== 'useContext'\n )\n return\n\n context.report({\n message:\n 'Destructuring useContext() captures values once — reactive getters lose reactivity. Keep the object reference: `const ctx = useContext(Ctx)` and access `ctx.mode` lazily.',\n span: getSpan(id),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nfunction isUpdateCall(node: any): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'MemberExpression' &&\n node.callee.property?.type === 'Identifier' &&\n node.callee.property.name === 'update'\n )\n}\n\nexport const noEffectAssignment: Rule = {\n meta: {\n id: 'pyreon/no-effect-assignment',\n category: 'reactivity',\n description: 'Warn when an effect only contains a single .update() call.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'effect')) return\n const args = node.arguments\n if (!args || args.length === 0) return\n\n const fn = args[0]\n if (!fn) return\n\n let body: any = null\n if (fn.type === 'ArrowFunctionExpression' || fn.type === 'FunctionExpression') {\n body = fn.body\n }\n if (!body) return\n\n // Arrow with expression body\n if (isUpdateCall(body)) {\n context.report({\n message:\n 'Effect contains a single `.update()` — consider using `computed()` for derived values.',\n span: getSpan(node),\n })\n return\n }\n\n // Block body with single statement\n if (body.type === 'BlockStatement') {\n const stmts = body.body\n if (stmts && stmts.length === 1) {\n const stmt = stmts[0]\n if (stmt.type === 'ExpressionStatement' && isUpdateCall(stmt.expression)) {\n context.report({\n message:\n 'Effect contains a single `.update()` — consider using `computed()` for derived values.',\n span: getSpan(node),\n })\n }\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noNestedEffect: Rule = {\n meta: {\n id: 'pyreon/no-nested-effect',\n category: 'reactivity',\n description: 'Warn against nesting effect() inside another effect().',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let effectDepth = 0\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'effect')) return\n if (effectDepth > 0) {\n context.report({\n message: 'Nested `effect()` — consider using `computed()` for derived values instead.',\n span: getSpan(node),\n })\n }\n effectDepth++\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'effect')) {\n effectDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo, isPeekCall } from '../../utils/ast'\n\nexport const noPeekInTracked: Rule = {\n meta: {\n id: 'pyreon/no-peek-in-tracked',\n category: 'reactivity',\n description: 'Disallow .peek() inside effect() or computed() — it bypasses tracking.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n let trackedDepth = 0\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (isCallTo(node, 'effect') || isCallTo(node, 'computed')) {\n trackedDepth++\n }\n if (trackedDepth > 0 && isPeekCall(node)) {\n context.report({\n message:\n '`.peek()` inside a tracked scope (effect/computed) bypasses dependency tracking — use a normal signal read instead.',\n span: getSpan(node),\n })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'effect') || isCallTo(node, 'computed')) {\n trackedDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noSignalInLoop: Rule = {\n meta: {\n id: 'pyreon/no-signal-in-loop',\n category: 'reactivity',\n description: 'Disallow creating signals or computeds inside loops.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n let loopDepth = 0\n const callbacks: VisitorCallbacks = {\n ForStatement() {\n loopDepth++\n },\n 'ForStatement:exit'() {\n loopDepth--\n },\n ForInStatement() {\n loopDepth++\n },\n 'ForInStatement:exit'() {\n loopDepth--\n },\n ForOfStatement() {\n loopDepth++\n },\n 'ForOfStatement:exit'() {\n loopDepth--\n },\n WhileStatement() {\n loopDepth++\n },\n 'WhileStatement:exit'() {\n loopDepth--\n },\n DoWhileStatement() {\n loopDepth++\n },\n 'DoWhileStatement:exit'() {\n loopDepth--\n },\n CallExpression(node: any) {\n if (loopDepth === 0) return\n const callee = node.callee\n if (!callee || callee.type !== 'Identifier') return\n if (callee.name === 'signal' || callee.name === 'computed') {\n context.report({\n message: `\\`${callee.name}()\\` inside a loop — signals should be created once at component setup, not on every iteration.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nfunction isComponentTag(name: string): boolean {\n return name.length > 0 && name[0] === name[0]?.toUpperCase() && name[0] !== name[0]?.toLowerCase()\n}\n\n/**\n * Warn when a known signal/computed is called in a component prop position.\n * Component props are evaluated once at mount — signal reads are NOT reactive\n * unless the compiler wraps them with _rp(). The compiler handles this\n * automatically, but this rule catches manual h() calls and educates developers.\n */\nexport const noSignalInProps: Rule = {\n meta: {\n id: 'pyreon/no-signal-in-props',\n category: 'reactivity',\n description:\n 'Signal call in component prop — value captured once unless compiler wraps it. Use props.x pattern for reactivity.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXExpressionContainer(node: any) {\n const expr = node.expression\n if (!expr || expr.type !== 'CallExpression') return\n const callee = expr.callee\n if (!callee || callee.type !== 'Identifier') return\n\n const source = context.getSourceText()\n const start = node.start as number\n\n let i = start - 1\n while (i >= 0 && source[i] !== '<' && source[i] !== '>') i--\n if (i < 0 || source[i] !== '<') return\n\n const tagStart = i + 1\n let tagEnd = tagStart\n while (tagEnd < source.length && /[\\w.]/.test(source[tagEnd] ?? '')) tagEnd++\n const tagName = source.slice(tagStart, tagEnd)\n\n if (!tagName || !isComponentTag(tagName)) return\n\n context.report({\n message: `Signal call in <${tagName}> prop — use props.x pattern inside the component for reactive access.`,\n span: getSpan(expr),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noSignalLeak: Rule = {\n meta: {\n id: 'pyreon/no-signal-leak',\n category: 'reactivity',\n description: 'Warn about unused signal declarations (potential leaks).',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const signalDecls = new Map<\n string,\n { span: { start: number; end: number }; declStart: number; declEnd: number }\n >()\n const identifierOccurrences = new Map<string, Array<{ start: number; end: number }>>()\n\n const callbacks: VisitorCallbacks = {\n VariableDeclarator(node: any) {\n const init = node.init\n if (!init || !isCallTo(init, 'signal')) return\n const id = node.id\n if (!id || id.type !== 'Identifier') return\n signalDecls.set(id.name, {\n span: getSpan(node),\n declStart: id.start as number,\n declEnd: id.end as number,\n })\n },\n Identifier(node: any) {\n const name: string = node.name\n const existing = identifierOccurrences.get(name)\n if (existing) {\n existing.push({ start: node.start as number, end: node.end as number })\n } else {\n identifierOccurrences.set(name, [\n { start: node.start as number, end: node.end as number },\n ])\n }\n },\n 'Program:exit'() {\n for (const [name, { span, declStart, declEnd }] of signalDecls) {\n const occurrences = identifierOccurrences.get(name) ?? []\n // Filter out the declaration identifier itself\n const usages = occurrences.filter((o) => o.start !== declStart || o.end !== declEnd)\n if (usages.length === 0) {\n context.report({\n message: `Signal \\`${name}\\` is declared but never used — this may be a signal leak.`,\n span,\n })\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo, isSetCall } from '../../utils/ast'\n\ninterface ScopeInfo {\n setCalls: Array<{ span: { start: number; end: number } }>\n hasBatch: boolean\n insideBatch: boolean\n node: any\n}\n\nexport const noUnbatchedUpdates: Rule = {\n meta: {\n id: 'pyreon/no-unbatched-updates',\n category: 'reactivity',\n description: 'Warn when 3+ .set() calls occur in the same function without batch().',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const scopeStack: ScopeInfo[] = []\n let batchDepth = 0\n\n function enterScope(node: any) {\n scopeStack.push({ setCalls: [], hasBatch: false, insideBatch: batchDepth > 0, node })\n }\n\n function exitScope() {\n const scope = scopeStack.pop()\n if (!scope) return\n if (!scope.hasBatch && !scope.insideBatch && scope.setCalls.length >= 3) {\n context.report({\n message: `${scope.setCalls.length} signal \\`.set()\\` calls without \\`batch()\\` — wrap in \\`batch(() => { ... })\\` to avoid unnecessary re-renders.`,\n span: getSpan(scope.node),\n })\n }\n }\n\n const callbacks: VisitorCallbacks = {\n FunctionDeclaration(node: any) {\n enterScope(node)\n },\n 'FunctionDeclaration:exit'() {\n exitScope()\n },\n FunctionExpression(node: any) {\n enterScope(node)\n },\n 'FunctionExpression:exit'() {\n exitScope()\n },\n ArrowFunctionExpression(node: any) {\n enterScope(node)\n },\n 'ArrowFunctionExpression:exit'() {\n exitScope()\n },\n CallExpression(node: any) {\n const currentScope = scopeStack.length > 0 ? scopeStack[scopeStack.length - 1] : undefined\n if (isCallTo(node, 'batch')) {\n batchDepth++\n if (currentScope) {\n currentScope.hasBatch = true\n }\n }\n if (currentScope && isSetCall(node)) {\n currentScope.setCalls.push({ span: getSpan(node) })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'batch')) {\n batchDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo, isSetCall } from '../../utils/ast'\n\nexport const preferComputed: Rule = {\n meta: {\n id: 'pyreon/prefer-computed',\n category: 'reactivity',\n description: 'Suggest computed() when an effect only contains a single .set() call.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'effect')) return\n const args = node.arguments\n if (!args || args.length === 0) return\n\n const fn = args[0]\n if (!fn) return\n\n let body: any = null\n if (fn.type === 'ArrowFunctionExpression' || fn.type === 'FunctionExpression') {\n body = fn.body\n }\n if (!body) return\n\n // Arrow with expression body: effect(() => x.set(y))\n if (body.type === 'CallExpression' && isSetCall(body)) {\n context.report({\n message:\n 'Effect contains a single `.set()` — consider using `computed()` instead for derived values.',\n span: getSpan(node),\n })\n return\n }\n\n // Block body with single statement: effect(() => { x.set(y) })\n if (body.type === 'BlockStatement') {\n const stmts = body.body\n if (stmts && stmts.length === 1) {\n const stmt = stmts[0]\n if (stmt.type === 'ExpressionStatement' && isSetCall(stmt.expression)) {\n context.report({\n message:\n 'Effect contains a single `.set()` — consider using `computed()` instead for derived values.',\n span: getSpan(node),\n })\n }\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getJSXAttribute, getSpan } from '../../utils/ast'\nimport { extractImportInfo } from '../../utils/imports'\n\nconst EXTERNAL_PREFIXES = ['http://', 'https://', 'mailto:', 'tel:']\n\nexport const noHrefNavigation: Rule = {\n meta: {\n id: 'pyreon/no-href-navigation',\n category: 'router',\n description:\n 'Warn when `<a href>` is used in files that import @pyreon/router — use `<Link>` instead.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let importsRouter = false\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (info && info.source === '@pyreon/router') {\n importsRouter = true\n }\n },\n JSXOpeningElement(node: any) {\n if (!importsRouter) return\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'a') return\n\n const hrefAttr = getJSXAttribute(node, 'href')\n if (!hrefAttr) return\n\n // Get the href value\n const value = hrefAttr.value\n if (value?.type === 'Literal' && typeof value.value === 'string') {\n const href: string = value.value\n // Skip external URLs and anchor links\n if (href.startsWith('#') || EXTERNAL_PREFIXES.some((p) => href.startsWith(p))) return\n }\n\n context.report({\n message:\n '`<a href>` in a router file — use `<Link>` or `<RouterLink>` for client-side navigation.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo, isMemberCallTo } from '../../utils/ast'\n\nexport const noImperativeNavigateInRender: Rule = {\n meta: {\n id: 'pyreon/no-imperative-navigate-in-render',\n category: 'router',\n description:\n 'Error when navigate() or router.push() is called at the top level of a component — causes infinite render loops.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n // Track depth of component functions and safe callback wrappers\n // We detect components via VariableDeclarator with PascalCase name + ArrowFunctionExpression init,\n // or FunctionDeclaration with PascalCase name.\n // \"Safe\" = onMount/effect/onUnmount callbacks or JSX event handlers.\n let componentBodyDepth = 0\n let safeDepth = 0\n\n const callbacks: VisitorCallbacks = {\n FunctionDeclaration(node: any) {\n const name: string = node.id?.name ?? ''\n if (/^[A-Z]/.test(name)) {\n componentBodyDepth++\n }\n },\n 'FunctionDeclaration:exit'(node: any) {\n const name: string = node.id?.name ?? ''\n if (/^[A-Z]/.test(name)) {\n componentBodyDepth--\n }\n },\n // For arrow functions, we use VariableDeclarator to detect component assignment\n VariableDeclarator(node: any) {\n const name: string = node.id?.name ?? ''\n if (/^[A-Z]/.test(name) && node.init?.type === 'ArrowFunctionExpression') {\n componentBodyDepth++\n }\n },\n 'VariableDeclarator:exit'(node: any) {\n const name: string = node.id?.name ?? ''\n if (/^[A-Z]/.test(name) && node.init?.type === 'ArrowFunctionExpression') {\n componentBodyDepth--\n }\n },\n // Track safe callback boundaries: onMount(() => ...), effect(() => ...), etc.\n CallExpression(node: any) {\n if (componentBodyDepth <= 0) return\n\n // Check if this is a safe wrapper entering\n if (isSafeWrapperCall(node)) {\n safeDepth++\n }\n\n // Only report if we're in a component body and NOT inside a safe callback\n if (safeDepth > 0) return\n\n if (isCallTo(node, 'navigate') || isMemberCallTo(node, 'router', 'push')) {\n context.report({\n message:\n 'Imperative navigation at the top level of a component — this runs on every render and causes infinite loops. Move inside `onMount`, `effect`, or an event handler.',\n span: getSpan(node),\n })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (componentBodyDepth <= 0) return\n if (isSafeWrapperCall(node)) {\n safeDepth--\n }\n },\n }\n return callbacks\n },\n}\n\nfunction isSafeWrapperCall(node: any): boolean {\n const callee = node.callee\n if (!callee || callee.type !== 'Identifier') return false\n const name: string = callee.name\n return name === 'onMount' || name === 'effect' || name === 'onUnmount'\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { extractImportInfo } from '../../utils/imports'\n\nfunction isCatchAllPath(value: string): boolean {\n return value === '*' || value.endsWith('*')\n}\n\nfunction getPathValue(prop: any): string | null {\n const key = prop.key\n if (!key) return null\n const keyName = key.type === 'Identifier' ? key.name : null\n if (keyName !== 'path') return null\n const val = prop.value\n if (val?.type === 'Literal' && typeof val.value === 'string') {\n return val.value\n }\n return null\n}\n\nfunction hasPathProperty(obj: any): boolean {\n if (!obj || obj.type !== 'ObjectExpression') return false\n for (const prop of obj.properties ?? []) {\n if (prop.type !== 'Property') continue\n if (getPathValue(prop) !== null) return true\n }\n return false\n}\n\nfunction hasCatchAllRoute(elements: any[]): boolean {\n for (const elem of elements) {\n if (!elem || elem.type !== 'ObjectExpression') continue\n for (const prop of elem.properties ?? []) {\n if (prop.type !== 'Property') continue\n const pathVal = getPathValue(prop)\n if (pathVal !== null && isCatchAllPath(pathVal)) return true\n }\n }\n return false\n}\n\nexport const noMissingFallback: Rule = {\n meta: {\n id: 'pyreon/no-missing-fallback',\n category: 'router',\n description:\n 'Warn when route config has no catch-all route (`path: \"*\"` or `path: \"/:rest*\"`).',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let importsRouter = false\n let routeArraySpan: { start: number; end: number } | null = null\n let foundCatchAll = false\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (info && info.source === '@pyreon/router') {\n importsRouter = true\n }\n },\n ArrayExpression(node: any) {\n if (!importsRouter) return\n const elements = node.elements ?? []\n const isRouteArray = elements.some((e: any) => hasPathProperty(e))\n if (!isRouteArray) return\n\n if (!routeArraySpan) {\n routeArraySpan = getSpan(node)\n }\n if (hasCatchAllRoute(elements)) {\n foundCatchAll = true\n }\n },\n 'Program:exit'() {\n if (!importsRouter || !routeArraySpan || foundCatchAll) return\n context.report({\n message:\n 'Route config has no catch-all route — add a `{ path: \"*\", component: NotFound }` for unmatched URLs.',\n span: routeArraySpan,\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const preferUseIsActive: Rule = {\n meta: {\n id: 'pyreon/prefer-use-is-active',\n category: 'router',\n description:\n 'Suggest useIsActive() instead of `location.pathname === \"/foo\"` or `route.path === \"/foo\"` patterns.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n BinaryExpression(node: any) {\n if (node.operator !== '===' && node.operator !== '==') return\n\n // Check both sides for location.pathname or route.path\n if (isPathComparison(node.left) || isPathComparison(node.right)) {\n context.report({\n message:\n 'Manual path comparison — use `useIsActive()` for reactive route matching with segment-aware prefix matching.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n\nfunction isPathComparison(node: any): boolean {\n if (!node || node.type !== 'MemberExpression') return false\n const obj = node.object\n const prop = node.property\n if (!obj || !prop || prop.type !== 'Identifier') return false\n\n // location.pathname\n if (obj.type === 'Identifier' && obj.name === 'location' && prop.name === 'pathname') return true\n\n // route.path\n if (obj.type === 'Identifier' && obj.name === 'route' && prop.name === 'path') return true\n\n return false\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isMemberCallTo } from '../../utils/ast'\n\nexport const noMismatchRisk: Rule = {\n meta: {\n id: 'pyreon/no-mismatch-risk',\n category: 'ssr',\n description:\n 'Warn about non-deterministic calls (Date.now, Math.random, crypto.randomUUID) in JSX context that cause hydration mismatches.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let jsxDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXElement() {\n jsxDepth++\n },\n 'JSXElement:exit'() {\n jsxDepth--\n },\n JSXFragment() {\n jsxDepth++\n },\n 'JSXFragment:exit'() {\n jsxDepth--\n },\n CallExpression(node: any) {\n if (jsxDepth === 0) return\n\n if (\n isMemberCallTo(node, 'Date', 'now') ||\n isMemberCallTo(node, 'Math', 'random') ||\n isMemberCallTo(node, 'crypto', 'randomUUID')\n ) {\n const callee = node.callee\n const name = `${callee.object.name}.${callee.property.name}`\n context.report({\n message: `\\`${name}()\\` in JSX context — this produces different values on server and client, causing hydration mismatches.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\nimport { BROWSER_GLOBALS } from '../../utils/imports'\n\nexport const noWindowInSsr: Rule = {\n meta: {\n id: 'pyreon/no-window-in-ssr',\n category: 'ssr',\n description: 'Disallow browser globals outside onMount/effect/typeof guards — they break SSR.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n let safeDepth = 0\n let typeofGuardDepth = 0\n\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (isCallTo(node, 'onMount') || isCallTo(node, 'effect')) {\n safeDepth++\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'onMount') || isCallTo(node, 'effect')) {\n safeDepth--\n }\n },\n IfStatement(node: any) {\n // typeof window !== \"undefined\"\n const test = node.test\n if (\n test?.type === 'BinaryExpression' &&\n test.left?.type === 'UnaryExpression' &&\n test.left.operator === 'typeof'\n ) {\n typeofGuardDepth++\n }\n },\n 'IfStatement:exit'(node: any) {\n const test = node.test\n if (\n test?.type === 'BinaryExpression' &&\n test.left?.type === 'UnaryExpression' &&\n test.left.operator === 'typeof'\n ) {\n typeofGuardDepth--\n }\n },\n Identifier(node: any, parent: any) {\n if (safeDepth > 0 || typeofGuardDepth > 0) return\n if (!BROWSER_GLOBALS.has(node.name)) return\n\n // Skip typeof expressions: typeof window\n if (parent?.type === 'UnaryExpression' && parent.operator === 'typeof') return\n\n // Skip import specifiers\n if (\n parent?.type === 'ImportSpecifier' ||\n parent?.type === 'ImportDefaultSpecifier' ||\n parent?.type === 'ImportNamespaceSpecifier'\n )\n return\n\n // Skip property access on member expressions (only flag when used as the object)\n if (parent?.type === 'MemberExpression' && parent.property === node && !parent.computed)\n return\n\n context.report({\n message: `Browser global \\`${node.name}\\` used outside \\`onMount\\`/\\`effect\\`/typeof guard — this will fail during SSR. Wrap in \\`onMount(() => { ... })\\`.`,\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const preferRequestContext: Rule = {\n meta: {\n id: 'pyreon/prefer-request-context',\n category: 'ssr',\n description:\n 'Warn about module-level signal()/createStore() in server files — use request context instead.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const filePath = context.getFilePath()\n const isServerFile =\n filePath.includes('server') ||\n filePath.includes('.server.') ||\n filePath.endsWith('server.ts') ||\n filePath.endsWith('server.tsx')\n\n if (!isServerFile) return {}\n\n let functionDepth = 0\n const callbacks: VisitorCallbacks = {\n FunctionDeclaration() {\n functionDepth++\n },\n 'FunctionDeclaration:exit'() {\n functionDepth--\n },\n FunctionExpression() {\n functionDepth++\n },\n 'FunctionExpression:exit'() {\n functionDepth--\n },\n ArrowFunctionExpression() {\n functionDepth++\n },\n 'ArrowFunctionExpression:exit'() {\n functionDepth--\n },\n CallExpression(node: any) {\n if (functionDepth > 0) return // only flag module-level calls\n if (isCallTo(node, 'signal') || isCallTo(node, 'createStore')) {\n const name = node.callee.name\n context.report({\n message: `Module-level \\`${name}()\\` in a server file — this state is shared across all requests. Use \\`runWithRequestContext()\\` for per-request isolation.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noDuplicateStoreId: Rule = {\n meta: {\n id: 'pyreon/no-duplicate-store-id',\n category: 'store',\n description: 'Disallow duplicate defineStore() IDs in the same file.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n const storeIds = new Map<string, { start: number; end: number }>()\n\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'defineStore')) return\n const args = node.arguments\n if (!args || args.length === 0) return\n\n const firstArg = args[0]\n if (!firstArg) return\n\n let id: string | null = null\n if (firstArg.type === 'Literal' || firstArg.type === 'StringLiteral') {\n id = firstArg.value as string\n }\n\n if (typeof id !== 'string') return\n\n if (storeIds.has(id)) {\n context.report({\n message: `Duplicate store ID \\`\"${id}\"\\` — each \\`defineStore()\\` must have a unique ID.`,\n span: getSpan(node),\n })\n } else {\n storeIds.set(id, getSpan(node))\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noMutateStoreState: Rule = {\n meta: {\n id: 'pyreon/no-mutate-store-state',\n category: 'store',\n description: 'Warn when directly calling .set() on store signals — use store actions instead.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n const callee = node.callee\n if (!callee || callee.type !== 'MemberExpression') return\n if (callee.property?.type !== 'Identifier' || callee.property.name !== 'set') return\n\n // Check for store.signal.set() pattern — member.member.set()\n const obj = callee.object\n if (!obj || obj.type !== 'MemberExpression') return\n const outerObj = obj.object\n if (!outerObj || outerObj.type !== 'Identifier') return\n\n const name: string = outerObj.name\n // Heuristic: if the outer object name contains \"store\" (case-insensitive)\n if (name.toLowerCase().includes('store')) {\n context.report({\n message: `Direct \\`.set()\\` on store state \\`${name}\\` — use store actions to mutate state for better traceability.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { extractImportInfo } from '../../utils/imports'\n\nexport const noStoreOutsideProvider: Rule = {\n meta: {\n id: 'pyreon/no-store-outside-provider',\n category: 'store',\n description: 'Warn when store hooks are used in SSR files without a provider import.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const filePath = context.getFilePath()\n const isServerFile =\n filePath.includes('server') ||\n filePath.includes('.server.') ||\n filePath.endsWith('server.ts') ||\n filePath.endsWith('server.tsx')\n\n if (!isServerFile) return {}\n\n let hasProviderImport = false\n const storeHookCalls: Array<{ name: string; span: { start: number; end: number } }> = []\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (!info) return\n if (\n info.specifiers.some(\n (s) =>\n s.imported === 'setStoreRegistryProvider' || s.imported === 'runWithRequestContext',\n )\n ) {\n hasProviderImport = true\n }\n },\n CallExpression(node: any) {\n const callee = node.callee\n if (!callee || callee.type !== 'Identifier') return\n const name: string = callee.name\n if (name.endsWith('Store') && name.startsWith('use')) {\n storeHookCalls.push({ name, span: getSpan(node) })\n }\n },\n 'Program:exit'() {\n if (hasProviderImport) return\n for (const call of storeHookCalls) {\n context.report({\n message: `\\`${call.name}()\\` in a server file without a store registry provider — use \\`runWithRequestContext()\\` or \\`setStoreRegistryProvider()\\` for SSR isolation.`,\n span: call.span,\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noDynamicStyled: Rule = {\n meta: {\n id: 'pyreon/no-dynamic-styled',\n category: 'styling',\n description:\n 'Warn when styled() is called inside a function — it creates new CSS on every render.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let functionDepth = 0\n const callbacks: VisitorCallbacks = {\n FunctionDeclaration() {\n functionDepth++\n },\n 'FunctionDeclaration:exit'() {\n functionDepth--\n },\n FunctionExpression() {\n functionDepth++\n },\n 'FunctionExpression:exit'() {\n functionDepth--\n },\n ArrowFunctionExpression() {\n functionDepth++\n },\n 'ArrowFunctionExpression:exit'() {\n functionDepth--\n },\n CallExpression(node: any) {\n if (functionDepth === 0) return\n if (isCallTo(node, 'styled')) {\n context.report({\n message:\n '`styled()` inside a function — this creates new CSS rules on every render. Move `styled()` to module scope.',\n span: getSpan(node),\n })\n }\n },\n TaggedTemplateExpression(node: any) {\n if (functionDepth === 0) return\n const tag = node.tag\n if (!tag) return\n // styled('div')`...` — tag is a CallExpression of styled\n if (tag.type === 'CallExpression' && isCallTo(tag, 'styled')) {\n context.report({\n message:\n '`styled()` tagged template inside a function — this creates new CSS rules on every render. Move to module scope.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noInlineStyleObject: Rule = {\n meta: {\n id: 'pyreon/no-inline-style-object',\n category: 'styling',\n description: 'Warn against inline style objects in JSX — prefer styled() or css``.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXAttribute(node: any) {\n if (node.name?.type !== 'JSXIdentifier' || node.name.name !== 'style') return\n const value = node.value\n if (!value || value.type !== 'JSXExpressionContainer') return\n const expr = value.expression\n if (expr?.type === 'ObjectExpression') {\n context.report({\n message:\n 'Inline style object in JSX — consider using `styled()` or `css\\\\`...\\\\`` for better performance and caching.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\nimport { extractImportInfo } from '../../utils/imports'\n\nexport const noThemeOutsideProvider: Rule = {\n meta: {\n id: 'pyreon/no-theme-outside-provider',\n category: 'styling',\n description: 'Warn when useTheme() is used without PyreonUI or ThemeProvider in the same file.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let hasProviderImport = false\n const themeCalls: Array<{ span: { start: number; end: number } }> = []\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (!info) return\n if (\n info.specifiers.some((s) => s.imported === 'PyreonUI' || s.imported === 'ThemeProvider')\n ) {\n hasProviderImport = true\n }\n },\n CallExpression(node: any) {\n if (isCallTo(node, 'useTheme')) {\n themeCalls.push({ span: getSpan(node) })\n }\n },\n 'Program:exit'() {\n if (hasProviderImport) return\n for (const call of themeCalls) {\n context.report({\n message:\n '`useTheme()` without a `PyreonUI` or `ThemeProvider` import — the theme context may not be available.',\n span: call.span,\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const preferCx: Rule = {\n meta: {\n id: 'pyreon/prefer-cx',\n category: 'styling',\n description:\n 'Suggest cx() for class composition instead of string concatenation or template literals.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXAttribute(node: any) {\n if (node.name?.type !== 'JSXIdentifier' || node.name.name !== 'class') return\n const value = node.value\n if (!value || value.type !== 'JSXExpressionContainer') return\n const expr = value.expression\n if (!expr) return\n\n // String concatenation: \"foo \" + bar\n if (expr.type === 'BinaryExpression' && expr.operator === '+') {\n context.report({\n message:\n 'String concatenation in `class` attribute — use `cx()` for cleaner class composition.',\n span: getSpan(expr),\n })\n return\n }\n\n // Template literal: `foo ${bar}`\n if (expr.type === 'TemplateLiteral' && expr.expressions?.length > 0) {\n context.report({\n message:\n 'Template literal in `class` attribute — use `cx()` for cleaner class composition.',\n span: getSpan(expr),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule } from '../types'\nimport { dialogA11y } from './accessibility/dialog-a11y'\nimport { overlayA11y } from './accessibility/overlay-a11y'\n// Accessibility\nimport { toastA11y } from './accessibility/toast-a11y'\nimport { devGuardWarnings } from './architecture/dev-guard-warnings'\n// Architecture\nimport { noCircularImport } from './architecture/no-circular-import'\nimport { noCrossLayerImport } from './architecture/no-cross-layer-import'\nimport { noDeepImport } from './architecture/no-deep-import'\nimport { noErrorWithoutPrefix } from './architecture/no-error-without-prefix'\nimport { noProcessDevGate } from './architecture/no-process-dev-gate'\nimport { noSubmitWithoutValidation } from './form/no-submit-without-validation'\n// Form\nimport { noUnregisteredField } from './form/no-unregistered-field'\nimport { preferFieldArray } from './form/prefer-field-array'\n// Hooks\nimport { noRawAddEventListener } from './hooks/no-raw-addeventlistener'\nimport { noRawLocalStorage } from './hooks/no-raw-localstorage'\nimport { noRawSetInterval } from './hooks/no-raw-setinterval'\nimport { noAndConditional } from './jsx/no-and-conditional'\nimport { noChildrenAccess } from './jsx/no-children-access'\nimport { noClassName } from './jsx/no-classname'\nimport { noHtmlFor } from './jsx/no-htmlfor'\nimport { noIndexAsBy } from './jsx/no-index-as-by'\n// JSX\nimport { noMapInJsx } from './jsx/no-map-in-jsx'\nimport { noMissingForBy } from './jsx/no-missing-for-by'\nimport { noOnChange } from './jsx/no-onchange'\nimport { noPropsDestructure } from './jsx/no-props-destructure'\nimport { noTernaryConditional } from './jsx/no-ternary-conditional'\nimport { useByNotKey } from './jsx/use-by-not-key'\nimport { noDomInSetup } from './lifecycle/no-dom-in-setup'\nimport { noEffectInMount } from './lifecycle/no-effect-in-mount'\n// Lifecycle\nimport { noMissingCleanup } from './lifecycle/no-missing-cleanup'\nimport { noMountInEffect } from './lifecycle/no-mount-in-effect'\nimport { noEagerImport } from './performance/no-eager-import'\nimport { noEffectInFor } from './performance/no-effect-in-for'\n// Performance\nimport { noLargeForWithoutBy } from './performance/no-large-for-without-by'\nimport { preferShowOverDisplay } from './performance/prefer-show-over-display'\n// Reactivity\nimport { noBareSignalInJsx } from './reactivity/no-bare-signal-in-jsx'\nimport { noContextDestructure } from './reactivity/no-context-destructure'\nimport { noEffectAssignment } from './reactivity/no-effect-assignment'\nimport { noNestedEffect } from './reactivity/no-nested-effect'\nimport { noPeekInTracked } from './reactivity/no-peek-in-tracked'\nimport { noSignalInLoop } from './reactivity/no-signal-in-loop'\nimport { noSignalInProps } from './reactivity/no-signal-in-props'\nimport { noSignalLeak } from './reactivity/no-signal-leak'\nimport { noUnbatchedUpdates } from './reactivity/no-unbatched-updates'\nimport { preferComputed } from './reactivity/prefer-computed'\n// Router\nimport { noHrefNavigation } from './router/no-href-navigation'\nimport { noImperativeNavigateInRender } from './router/no-imperative-navigate-in-render'\nimport { noMissingFallback } from './router/no-missing-fallback'\nimport { preferUseIsActive } from './router/prefer-use-is-active'\nimport { noMismatchRisk } from './ssr/no-mismatch-risk'\n// SSR\nimport { noWindowInSsr } from './ssr/no-window-in-ssr'\nimport { preferRequestContext } from './ssr/prefer-request-context'\nimport { noDuplicateStoreId } from './store/no-duplicate-store-id'\nimport { noMutateStoreState } from './store/no-mutate-store-state'\n// Store\nimport { noStoreOutsideProvider } from './store/no-store-outside-provider'\nimport { noDynamicStyled } from './styling/no-dynamic-styled'\n// Styling\nimport { noInlineStyleObject } from './styling/no-inline-style-object'\nimport { noThemeOutsideProvider } from './styling/no-theme-outside-provider'\nimport { preferCx } from './styling/prefer-cx'\n\nexport const allRules: Rule[] = [\n // Reactivity (10)\n noBareSignalInJsx,\n noContextDestructure,\n noSignalInLoop,\n noSignalInProps,\n noNestedEffect,\n noPeekInTracked,\n noUnbatchedUpdates,\n preferComputed,\n noEffectAssignment,\n noSignalLeak,\n // JSX (11)\n noMapInJsx,\n useByNotKey,\n noClassName,\n noHtmlFor,\n noOnChange,\n noTernaryConditional,\n noAndConditional,\n noIndexAsBy,\n noMissingForBy,\n noPropsDestructure,\n noChildrenAccess,\n // Lifecycle (4)\n noMissingCleanup,\n noMountInEffect,\n noEffectInMount,\n noDomInSetup,\n // Performance (4)\n noLargeForWithoutBy,\n noEffectInFor,\n noEagerImport,\n preferShowOverDisplay,\n // SSR (3)\n noWindowInSsr,\n noMismatchRisk,\n preferRequestContext,\n // Architecture (6)\n noCircularImport,\n noDeepImport,\n noCrossLayerImport,\n devGuardWarnings,\n noErrorWithoutPrefix,\n noProcessDevGate,\n // Store (3)\n noStoreOutsideProvider,\n noMutateStoreState,\n noDuplicateStoreId,\n // Form (3)\n noUnregisteredField,\n noSubmitWithoutValidation,\n preferFieldArray,\n // Styling (4)\n noInlineStyleObject,\n noDynamicStyled,\n preferCx,\n noThemeOutsideProvider,\n // Hooks (3)\n noRawAddEventListener,\n noRawSetInterval,\n noRawLocalStorage,\n // Accessibility (3)\n toastA11y,\n dialogA11y,\n overlayA11y,\n // Router (4)\n noHrefNavigation,\n noImperativeNavigateInRender,\n noMissingFallback,\n preferUseIsActive,\n]\n\n// Re-export all rules individually\nexport {\n devGuardWarnings,\n dialogA11y,\n noAndConditional,\n // Reactivity\n noBareSignalInJsx,\n noContextDestructure,\n noChildrenAccess,\n // Architecture\n noCircularImport,\n noClassName,\n noCrossLayerImport,\n noDeepImport,\n noDomInSetup,\n noDuplicateStoreId,\n noDynamicStyled,\n noEagerImport,\n noEffectAssignment,\n noEffectInFor,\n noEffectInMount,\n noErrorWithoutPrefix,\n noHrefNavigation,\n noHtmlFor,\n noImperativeNavigateInRender,\n noIndexAsBy,\n // Styling\n noInlineStyleObject,\n // Performance\n noLargeForWithoutBy,\n // JSX\n noMapInJsx,\n noMismatchRisk,\n // Lifecycle\n noMissingCleanup,\n noMissingFallback,\n noMissingForBy,\n noMountInEffect,\n noMutateStoreState,\n noNestedEffect,\n noOnChange,\n noPeekInTracked,\n noProcessDevGate,\n noPropsDestructure,\n // Hooks\n noRawAddEventListener,\n noRawLocalStorage,\n noRawSetInterval,\n noSignalInLoop,\n noSignalInProps,\n noSignalLeak,\n // Store\n noStoreOutsideProvider,\n noSubmitWithoutValidation,\n noTernaryConditional,\n noThemeOutsideProvider,\n noUnbatchedUpdates,\n // Form\n noUnregisteredField,\n // SSR\n noWindowInSsr,\n overlayA11y,\n preferComputed,\n preferCx,\n preferFieldArray,\n preferRequestContext,\n preferShowOverDisplay,\n preferUseIsActive,\n // Accessibility\n toastA11y,\n useByNotKey,\n}\n","import { allRules } from '../rules/index'\nimport type { LintConfig, PresetName, Severity } from '../types'\n\n/** Build a config where every rule uses its default severity. */\nfunction buildRecommended(): LintConfig {\n const rules: Record<string, Severity> = {}\n for (const rule of allRules) {\n rules[rule.meta.id] = rule.meta.severity\n }\n return { rules }\n}\n\n/** Build a config where every warn is promoted to error. */\nfunction buildStrict(): LintConfig {\n const base = buildRecommended()\n const rules: Record<string, Severity> = {}\n for (const [id, sev] of Object.entries(base.rules)) {\n rules[id] = sev === 'warn' ? 'error' : sev\n }\n return { rules }\n}\n\n/** Build app config — recommended but disable library-only rules. */\nfunction buildApp(): LintConfig {\n const base = buildRecommended()\n return {\n rules: {\n ...base.rules,\n 'pyreon/dev-guard-warnings': 'off',\n 'pyreon/no-error-without-prefix': 'off',\n 'pyreon/no-circular-import': 'off',\n 'pyreon/no-cross-layer-import': 'off',\n // `no-process-dev-gate` stays ON in `app` preset because the bug\n // hits user-facing browser code regardless of whether it's a lib\n // or an app.\n },\n }\n}\n\n/** Build lib config — strict + all architecture rules as error. */\nfunction buildLib(): LintConfig {\n const base = buildStrict()\n return {\n rules: {\n ...base.rules,\n 'pyreon/no-circular-import': 'error',\n 'pyreon/no-cross-layer-import': 'error',\n 'pyreon/dev-guard-warnings': 'error',\n 'pyreon/no-error-without-prefix': 'error',\n 'pyreon/no-process-dev-gate': 'error',\n },\n }\n}\n\nconst presetBuilders: Record<PresetName, () => LintConfig> = {\n recommended: buildRecommended,\n strict: buildStrict,\n app: buildApp,\n lib: buildLib,\n}\n\nexport function getPreset(name: PresetName): LintConfig {\n return presetBuilders[name]()\n}\n\nexport { buildApp, buildLib, buildRecommended, buildStrict }\n","import type { SourceLocation } from '../types'\n\n/**\n * Fast offset→line/column conversion using binary search over precomputed line starts.\n */\nexport class LineIndex {\n private lineStarts: number[]\n\n constructor(sourceText: string) {\n this.lineStarts = [0]\n for (let i = 0; i < sourceText.length; i++) {\n if (sourceText[i] === '\\n') {\n this.lineStarts.push(i + 1)\n }\n }\n }\n\n /** Convert a byte offset to a 1-based line and 0-based column. */\n locate(offset: number): SourceLocation {\n let lo = 0\n let hi = this.lineStarts.length - 1\n\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1\n if ((this.lineStarts[mid] as number) <= offset) {\n lo = mid + 1\n } else {\n hi = mid - 1\n }\n }\n\n const line = lo // 1-based (lo points one past the found index)\n const column = offset - (this.lineStarts[line - 1] as number)\n return { line, column }\n }\n}\n","export {\n getJSXAttribute,\n getJSXTagName,\n getSpan,\n hasJSXAttribute,\n hasJSXChild,\n isArrayMapCall,\n isBrowserGlobal,\n isCallTo,\n isCallToAny,\n isDestructuring,\n isFunction,\n isInsideDevGuard,\n isInsideFunction,\n isInsideJSX,\n isInsideOnMount,\n isInsideTypeofGuard,\n isJSXElement,\n isLogicalAndWithJSX,\n isMemberCallTo,\n isPeekCall,\n isSetCall,\n isTernaryWithJSX,\n} from './ast'\nexport {\n BROWSER_GLOBALS,\n CONTEXT_APIS,\n extractImportInfo,\n getLocalName,\n HEAVY_PACKAGES,\n importsName,\n isPyreonImport,\n isPyreonPackage,\n JSX_COMPONENTS,\n LIFECYCLE_APIS,\n PYREON_PREFIX,\n REACTIVITY_APIS,\n} from './imports'\nexport { LineIndex } from './source'\n\n/** Supported JS/TS file extensions for linting. */\nexport const JS_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mts', '.mjs'])\n\n/** Check if a file path has a supported JS/TS extension. */\nexport function hasJsExtension(filePath: string): boolean {\n const ext = filePath.slice(filePath.lastIndexOf('.'))\n return JS_EXTENSIONS.has(ext)\n}\n","import { parseSync, Visitor } from 'oxc-parser'\nimport type { AstCache } from './cache'\nimport type {\n Diagnostic,\n LintConfig,\n LintFileResult,\n Rule,\n RuleContext,\n Severity,\n VisitorCallbacks,\n} from './types'\nimport { JS_EXTENSIONS } from './utils/index'\nimport { LineIndex } from './utils/source'\n\nfunction getExtension(filePath: string): string {\n const lastDot = filePath.lastIndexOf('.')\n return lastDot === -1 ? '' : filePath.slice(lastDot)\n}\n\ntype OxcLang = 'jsx' | 'tsx' | 'ts' | 'js' | 'dts'\n\nfunction getLang(ext: string): OxcLang {\n if (ext === '.tsx' || ext === '.jsx') return 'tsx'\n if (ext === '.ts' || ext === '.mts') return 'ts'\n return 'js'\n}\n\nfunction createRuleContext(\n rule: Rule,\n severity: Severity,\n diagnostics: Diagnostic[],\n lineIndex: LineIndex,\n sourceText: string,\n filePath: string,\n): RuleContext {\n return {\n report(partial) {\n diagnostics.push({\n ruleId: rule.meta.id,\n severity,\n message: partial.message,\n span: partial.span,\n loc: lineIndex.locate(partial.span.start),\n fix: partial.fix,\n })\n },\n getSourceText() {\n return sourceText\n },\n getFilePath() {\n return filePath\n },\n }\n}\n\nfunction mergeCallbacks(allCallbacks: VisitorCallbacks[]): Record<string, (node: any) => void> {\n const callbacksByKey: Record<string, Array<(node: any) => void>> = {}\n\n for (const callbacks of allCallbacks) {\n for (const [key, fn] of Object.entries(callbacks)) {\n const existing = callbacksByKey[key]\n if (existing) {\n existing.push(fn as (node: any) => void)\n } else {\n callbacksByKey[key] = [fn as (node: any) => void]\n }\n }\n }\n\n const merged: Record<string, (node: any) => void> = {}\n for (const [key, fns] of Object.entries(callbacksByKey)) {\n const first = fns[0]\n if (fns.length === 1 && first) {\n merged[key] = first\n } else {\n merged[key] = (node: any) => {\n for (const fn of fns) fn(node)\n }\n }\n }\n return merged\n}\n\n/**\n * Lint a single file and return diagnostics.\n *\n * @example\n * ```ts\n * const result = lintFile(\"app.tsx\", source, allRules, getPreset(\"recommended\"))\n * for (const d of result.diagnostics) console.log(d.message)\n * ```\n */\nexport function lintFile(\n filePath: string,\n sourceText: string,\n rules: Rule[],\n config: LintConfig,\n cache?: AstCache | undefined,\n): LintFileResult {\n const ext = getExtension(filePath)\n if (!JS_EXTENSIONS.has(ext)) {\n return { filePath, diagnostics: [] }\n }\n\n // Try cache first\n let lineIndex: LineIndex\n let program: any\n const cached = cache?.get(sourceText)\n if (cached) {\n lineIndex = cached.lineIndex\n program = cached.program\n } else {\n lineIndex = new LineIndex(sourceText)\n try {\n const result = parseSync(filePath, sourceText, {\n sourceType: 'module',\n lang: getLang(ext),\n })\n program = result.program\n } catch {\n return { filePath, diagnostics: [] }\n }\n cache?.set(sourceText, { program, lineIndex })\n }\n\n const diagnostics: Diagnostic[] = []\n\n // Filter to enabled rules and create visitor callbacks\n const allCallbacks: VisitorCallbacks[] = []\n for (const rule of rules) {\n const severity = config.rules[rule.meta.id]\n if (severity === undefined || severity === 'off') continue\n const ctx = createRuleContext(rule, severity, diagnostics, lineIndex, sourceText, filePath)\n allCallbacks.push(rule.create(ctx))\n }\n\n // Walk the AST\n const visitor = new Visitor(mergeCallbacks(allCallbacks))\n visitor.visit(program)\n\n // Filter suppressed diagnostics:\n // // pyreon-lint-ignore — suppress all on next line\n // // pyreon-lint-ignore rule-name — suppress specific rule on next line\n const lines = sourceText.split('\\n')\n const filtered = diagnostics.filter((d) => {\n const prevLineIdx = d.loc.line - 2\n if (prevLineIdx < 0) return true\n const prevLine = lines[prevLineIdx]?.trim()\n if (!prevLine?.startsWith('// pyreon-lint-ignore')) return true\n const rest = prevLine.slice('// pyreon-lint-ignore'.length).trim()\n return rest.length > 0 && rest !== d.ruleId\n })\n\n filtered.sort((a, b) => a.span.start - b.span.start)\n return { filePath, diagnostics: filtered }\n}\n\n/**\n * Apply all auto-fixes to a source text.\n * Fixes are applied in reverse order to maintain correct offsets.\n */\nexport function applyFixes(sourceText: string, diagnostics: Diagnostic[]): string {\n const fixable = diagnostics.filter((d) => d.fix !== undefined)\n if (fixable.length === 0) return sourceText\n\n // Sort by start position descending (apply from end to start)\n const sorted = [...fixable].sort((a, b) => {\n const aFix = a.fix\n const bFix = b.fix\n if (!aFix || !bFix) return 0\n return bFix.span.start - aFix.span.start\n })\n\n let result = sourceText\n for (const diag of sorted) {\n const fix = diag.fix\n if (!fix) continue\n result = result.slice(0, fix.span.start) + fix.replacement + result.slice(fix.span.end)\n }\n\n return result\n}\n","import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { AstCache } from './cache'\nimport { createIgnoreFilter } from './config/ignore'\nimport { loadConfig, loadConfigFromPath } from './config/loader'\nimport { getPreset } from './config/presets'\nimport { allRules } from './rules/index'\nimport { applyFixes, lintFile } from './runner'\nimport type { LintConfig, LintFileResult, LintOptions, LintResult, RuleMeta } from './types'\nimport { hasJsExtension } from './utils/index'\n\nfunction isHiddenOrVendor(entry: string): boolean {\n return entry.startsWith('.') || entry === 'node_modules' || entry === 'lib' || entry === 'dist'\n}\n\nfunction matchesPatterns(\n filePath: string,\n include?: string[] | undefined,\n exclude?: string[] | undefined,\n): boolean {\n if (exclude) {\n for (const pattern of exclude) {\n if (filePath.includes(pattern)) return false\n }\n }\n if (include && include.length > 0) {\n for (const pattern of include) {\n if (filePath.includes(pattern)) return true\n }\n return false\n }\n return true\n}\n\nfunction walkDirectory(\n dir: string,\n files: string[],\n isIgnored: (filePath: string) => boolean,\n include?: string[] | undefined,\n exclude?: string[] | undefined,\n): void {\n let entries: string[]\n try {\n entries = readdirSync(dir)\n } catch {\n return\n }\n for (const entry of entries) {\n if (isHiddenOrVendor(entry)) continue\n const full = join(dir, entry)\n if (isIgnored(full)) continue\n processEntry(full, files, isIgnored, include, exclude)\n }\n}\n\nfunction processEntry(\n full: string,\n files: string[],\n isIgnored: (filePath: string) => boolean,\n include?: string[] | undefined,\n exclude?: string[] | undefined,\n): void {\n let stat: ReturnType<typeof statSync>\n try {\n stat = statSync(full)\n } catch {\n return\n }\n if (stat.isDirectory()) {\n walkDirectory(full, files, isIgnored, include, exclude)\n } else if (stat.isFile() && hasJsExtension(full) && matchesPatterns(full, include, exclude)) {\n files.push(full)\n }\n}\n\nfunction collectFiles(\n dir: string,\n isIgnored: (filePath: string) => boolean,\n include?: string[] | undefined,\n exclude?: string[] | undefined,\n): string[] {\n const files: string[] = []\n walkDirectory(dir, files, isIgnored, include, exclude)\n return files\n}\n\nfunction buildConfig(options: LintOptions): {\n config: LintConfig\n include: string[] | undefined\n exclude: string[] | undefined\n isIgnored: (filePath: string) => boolean\n} {\n const cwd = resolve('.')\n const fileConfig = options.config ? loadConfigFromPath(options.config) : loadConfig(cwd)\n\n const presetName = options.preset ?? fileConfig?.preset ?? 'recommended'\n const config = getPreset(presetName)\n\n // Merge config file rule overrides\n if (fileConfig?.rules) {\n for (const [id, severity] of Object.entries(fileConfig.rules)) {\n config.rules[id] = severity\n }\n }\n\n // CLI rule overrides (highest priority)\n if (options.ruleOverrides) {\n for (const [id, severity] of Object.entries(options.ruleOverrides)) {\n config.rules[id] = severity\n }\n }\n\n return {\n config,\n include: fileConfig?.include,\n exclude: fileConfig?.exclude,\n isIgnored: createIgnoreFilter(cwd, options.ignore),\n }\n}\n\nfunction gatherFiles(\n paths: string[],\n isIgnored: (filePath: string) => boolean,\n include?: string[] | undefined,\n exclude?: string[] | undefined,\n): string[] {\n const files: string[] = []\n for (const p of paths) {\n const resolved = resolve(p)\n let stat: ReturnType<typeof statSync>\n try {\n stat = statSync(resolved)\n } catch {\n continue\n }\n if (stat.isDirectory()) {\n files.push(...collectFiles(resolved, isIgnored, include, exclude))\n } else if (stat.isFile() && !isIgnored(resolved)) {\n files.push(resolved)\n }\n }\n return files\n}\n\nfunction applyFixesToFile(fileResult: LintFileResult, source: string): void {\n const fixable = fileResult.diagnostics.filter((d) => d.fix)\n if (fixable.length === 0) return\n const fixed = applyFixes(source, fileResult.diagnostics)\n writeFileSync(fileResult.filePath, fixed, 'utf-8')\n fileResult.fixedSource = fixed\n fileResult.diagnostics = fileResult.diagnostics.filter((d) => !d.fix)\n}\n\nfunction countDiagnostics(fileResult: LintFileResult, results: LintResult): void {\n for (const d of fileResult.diagnostics) {\n if (d.severity === 'error') results.totalErrors++\n else if (d.severity === 'warn') results.totalWarnings++\n else if (d.severity === 'info') results.totalInfos++\n }\n}\n\n/**\n * Lint files and return results.\n *\n * @example\n * ```ts\n * import { lint } from \"@pyreon/lint\"\n *\n * const result = lint({ paths: [\"src/\"], preset: \"recommended\" })\n * console.log(result.totalErrors) // 0\n * ```\n */\nexport function lint(options: LintOptions): LintResult {\n const { config, include, exclude, isIgnored } = buildConfig(options)\n const cache = new AstCache()\n const files = gatherFiles(options.paths, isIgnored, include, exclude)\n\n const results: LintResult = {\n files: [],\n totalErrors: 0,\n totalWarnings: 0,\n totalInfos: 0,\n }\n\n for (const filePath of files) {\n let source: string\n try {\n source = readFileSync(filePath, 'utf-8')\n } catch {\n continue\n }\n const fileResult = lintFile(filePath, source, allRules, config, cache)\n if (options.fix) {\n applyFixesToFile(fileResult, source)\n }\n if (options.quiet) {\n fileResult.diagnostics = fileResult.diagnostics.filter((d) => d.severity === 'error')\n }\n countDiagnostics(fileResult, results)\n results.files.push(fileResult)\n }\n\n return results\n}\n\n/**\n * List all available rules with their metadata.\n *\n * @example\n * ```ts\n * import { listRules } from \"@pyreon/lint\"\n *\n * for (const rule of listRules()) {\n * console.log(`${rule.id} (${rule.severity}): ${rule.description}`)\n * }\n * ```\n */\nexport function listRules(): RuleMeta[] {\n return allRules.map((r) => r.meta)\n}\n","import type { LintResult, Severity } from './types'\n\n// ANSI colors\nconst BOLD = '\\x1b[1m'\nconst RED = '\\x1b[31m'\nconst YELLOW = '\\x1b[33m'\nconst BLUE = '\\x1b[34m'\nconst DIM = '\\x1b[2m'\nconst RESET = '\\x1b[0m'\n\nconst SEVERITY_SYMBOL: Record<Severity, string> = {\n error: `${RED}\\u2716${RESET}`,\n warn: `${YELLOW}\\u26A0${RESET}`,\n info: `${BLUE}\\u2139${RESET}`,\n off: '',\n}\n\nconst SEVERITY_LABEL: Record<Severity, string> = {\n error: `${RED}error${RESET}`,\n warn: `${YELLOW}warning${RESET}`,\n info: `${BLUE}info${RESET}`,\n off: '',\n}\n\n/**\n * Format results as human-readable colored text.\n */\nexport function formatText(result: LintResult): string {\n const lines: string[] = []\n\n for (const file of result.files) {\n if (file.diagnostics.length === 0) continue\n\n lines.push('')\n lines.push(`${BOLD}${file.filePath}${RESET}`)\n\n for (const d of file.diagnostics) {\n const loc = `${DIM}${d.loc.line}:${d.loc.column}${RESET}`\n const severity = SEVERITY_LABEL[d.severity]\n const ruleId = `${DIM}${d.ruleId}${RESET}`\n lines.push(` ${loc} ${severity} ${d.message} ${ruleId}`)\n }\n }\n\n const total = result.totalErrors + result.totalWarnings + result.totalInfos\n if (total > 0) {\n lines.push('')\n const parts: string[] = []\n if (result.totalErrors > 0)\n parts.push(`${RED}${result.totalErrors} error${result.totalErrors === 1 ? '' : 's'}${RESET}`)\n if (result.totalWarnings > 0)\n parts.push(\n `${YELLOW}${result.totalWarnings} warning${result.totalWarnings === 1 ? '' : 's'}${RESET}`,\n )\n if (result.totalInfos > 0) parts.push(`${BLUE}${result.totalInfos} info${RESET}`)\n lines.push(`${SEVERITY_SYMBOL.error} ${parts.join(', ')}`)\n lines.push('')\n }\n\n return lines.join('\\n')\n}\n\n/**\n * Format results as JSON.\n */\nexport function formatJSON(result: LintResult): string {\n return JSON.stringify(result, null, 2)\n}\n\n/**\n * Format results as compact single-line-per-diagnostic output.\n */\nexport function formatCompact(result: LintResult): string {\n const lines: string[] = []\n\n for (const file of result.files) {\n for (const d of file.diagnostics) {\n lines.push(\n `${file.filePath}:${d.loc.line}:${d.loc.column}: ${d.severity} [${d.ruleId}] ${d.message}`,\n )\n }\n }\n\n return lines.join('\\n')\n}\n","/**\n * Minimal LSP server for @pyreon/lint.\n *\n * Provides real-time Pyreon-specific diagnostics in editors that support\n * the Language Server Protocol (VS Code, Neovim, etc.).\n *\n * Usage: pyreon-lint --lsp\n *\n * The server communicates via JSON-RPC over stdin/stdout following the\n * LSP specification (https://microsoft.github.io/language-server-protocol/).\n *\n * Supported capabilities:\n * - textDocument/didOpen — lint on open\n * - textDocument/didSave — lint on save\n * - textDocument/didChange — lint on change (debounced)\n *\n * @module\n */\n\nimport { AstCache } from '../cache'\nimport { getPreset } from '../config/presets'\nimport { allRules } from '../rules/index'\nimport { lintFile } from '../runner'\nimport type { Diagnostic, LintConfig } from '../types'\n\nconst cache = new AstCache()\nconst config: LintConfig = getPreset('recommended')\n\n// ─── JSON-RPC message types ────────────────────────────────────────────────\n\ninterface JsonRpcMessage {\n jsonrpc: '2.0'\n id?: number | string | undefined\n method?: string | undefined\n params?: any\n result?: any\n}\n\n// ─── LSP Diagnostic conversion ─────────────────────────────────────────────\n\ninterface LspDiagnostic {\n range: { start: { line: number; character: number }; end: { line: number; character: number } }\n severity: number\n source: string\n message: string\n code: string\n}\n\nfunction toLspDiagnostics(diagnostics: Diagnostic[]): LspDiagnostic[] {\n return diagnostics.map((d) => ({\n range: {\n start: { line: d.loc.line - 1, character: d.loc.column - 1 },\n end: { line: d.loc.line - 1, character: d.loc.column - 1 + (d.span.end - d.span.start) },\n },\n severity: d.severity === 'error' ? 1 : d.severity === 'warn' ? 2 : 3,\n source: 'pyreon-lint',\n message: d.message,\n code: d.ruleId,\n }))\n}\n\n// ─── Lint a document ───────────────────────────────────────────────────────\n\nfunction lintDocument(uri: string, text: string): LspDiagnostic[] {\n try {\n const filePath = uri.replace('file://', '')\n const result = lintFile(filePath, text, allRules, config, cache)\n return toLspDiagnostics(result.diagnostics)\n } catch {\n // Parse errors, unsupported file types — return empty diagnostics\n return []\n }\n}\n\n// ─── Debounce ──────────────────────────────────────────────────────────────\n\nconst DEBOUNCE_MS = 150\nconst debounceTimers = new Map<string, ReturnType<typeof setTimeout>>()\n\nfunction debounceLint(uri: string, text: string): void {\n const existing = debounceTimers.get(uri)\n if (existing) clearTimeout(existing)\n debounceTimers.set(\n uri,\n setTimeout(() => {\n debounceTimers.delete(uri)\n const diagnostics = lintDocument(uri, text)\n sendNotification('textDocument/publishDiagnostics', { uri, diagnostics })\n }, DEBOUNCE_MS),\n )\n}\n\n// ─── Message handling ──────────────────────────────────────────────────────\n\nconst openDocuments = new Map<string, string>()\n\nfunction handleMessage(msg: JsonRpcMessage): JsonRpcMessage | null {\n if (msg.method === 'initialize') {\n return {\n jsonrpc: '2.0',\n id: msg.id,\n result: {\n capabilities: {\n textDocumentSync: 1, // Full sync\n diagnosticProvider: { interFileDependencies: false, workspaceDiagnostics: false },\n },\n serverInfo: { name: 'pyreon-lint', version: '0.11.5' },\n },\n }\n }\n\n if (msg.method === 'initialized') {\n return null // no response needed\n }\n\n if (msg.method === 'textDocument/didOpen') {\n const { uri, text } = msg.params.textDocument\n openDocuments.set(uri, text)\n const diagnostics = lintDocument(uri, text)\n sendNotification('textDocument/publishDiagnostics', { uri, diagnostics })\n return null\n }\n\n if (msg.method === 'textDocument/didChange') {\n const uri = msg.params.textDocument.uri\n const text = msg.params.contentChanges[0]?.text\n if (text != null) {\n openDocuments.set(uri, text)\n // Debounce: wait 150ms after last keystroke before linting\n debounceLint(uri, text)\n }\n return null\n }\n\n if (msg.method === 'textDocument/didSave') {\n const uri = msg.params.textDocument.uri\n const text = openDocuments.get(uri)\n if (text) {\n const diagnostics = lintDocument(uri, text)\n sendNotification('textDocument/publishDiagnostics', { uri, diagnostics })\n }\n return null\n }\n\n if (msg.method === 'textDocument/didClose') {\n const uri = msg.params.textDocument.uri\n openDocuments.delete(uri)\n sendNotification('textDocument/publishDiagnostics', { uri, diagnostics: [] })\n return null\n }\n\n if (msg.method === 'shutdown') {\n return { jsonrpc: '2.0', id: msg.id, result: null }\n }\n\n if (msg.method === 'exit') {\n process.exit(0)\n }\n\n // Unknown method\n if (msg.id != null) {\n return {\n jsonrpc: '2.0',\n id: msg.id,\n result: null,\n }\n }\n\n return null\n}\n\n// ─── JSON-RPC transport (stdin/stdout) ─────────────────────────────────────\n\nfunction sendMessage(msg: JsonRpcMessage) {\n const body = JSON.stringify(msg)\n const header = `Content-Length: ${Buffer.byteLength(body)}\\r\\n\\r\\n`\n process.stdout.write(header + body)\n}\n\nfunction sendNotification(method: string, params: any) {\n sendMessage({ jsonrpc: '2.0', method, params })\n}\n\n/**\n * Start the LSP server. Reads JSON-RPC messages from stdin,\n * processes them, and writes responses to stdout.\n */\nexport function startLspServer(): void {\n let buffer = ''\n\n process.stdin.setEncoding('utf-8')\n process.stdin.on('data', (chunk: string) => {\n buffer += chunk\n\n // Parse Content-Length header + body\n while (true) {\n const headerEnd = buffer.indexOf('\\r\\n\\r\\n')\n if (headerEnd === -1) break\n\n const header = buffer.slice(0, headerEnd)\n const match = header.match(/Content-Length:\\s*(\\d+)/i)\n if (!match) {\n buffer = buffer.slice(headerEnd + 4)\n continue\n }\n\n const contentLength = Number.parseInt(match[1]!, 10)\n const bodyStart = headerEnd + 4\n if (buffer.length < bodyStart + contentLength) break\n\n const body = buffer.slice(bodyStart, bodyStart + contentLength)\n buffer = buffer.slice(bodyStart + contentLength)\n\n try {\n const msg = JSON.parse(body) as JsonRpcMessage\n const response = handleMessage(msg)\n if (response) sendMessage(response)\n } catch {\n // malformed JSON — ignore\n }\n }\n })\n\n process.stderr.write('[pyreon-lint] LSP server started\\n')\n}\n","import { readFileSync, watch } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { AstCache } from './cache'\nimport { createIgnoreFilter } from './config/ignore'\nimport { getPreset } from './config/presets'\nimport { formatCompact, formatJSON, formatText } from './reporter'\nimport { allRules } from './rules/index'\nimport { lintFile } from './runner'\nimport type { LintConfig, LintOptions, LintResult, Severity } from './types'\nimport { hasJsExtension } from './utils/index'\n\nfunction formatOutput(result: LintResult, format: string): string {\n if (format === 'json') return formatJSON(result)\n if (format === 'compact') return formatCompact(result)\n return formatText(result)\n}\n\n/**\n * Watch directories and re-lint changed files.\n *\n * Uses `fs.watch` (recursive) with 100ms debounce.\n * Caches ASTs for unchanged files.\n *\n * @example\n * ```ts\n * import { watchAndLint } from \"@pyreon/lint\"\n *\n * watchAndLint({ paths: [\"src/\"], preset: \"recommended\", format: \"text\" })\n * ```\n */\nexport function watchAndLint(options: LintOptions & { format: string }): void {\n const cache = new AstCache()\n const preset = options.preset ?? 'recommended'\n const config = getPreset(preset)\n\n applyOverrides(config, options.ruleOverrides)\n\n const cwd = resolve('.')\n const isIgnored = createIgnoreFilter(cwd, options.ignore)\n\n // Debounce map: filePath -> timeout\n const pending = new Map<string, ReturnType<typeof setTimeout>>()\n\n // oxlint-disable-next-line no-console\n console.log(`\\x1b[2m[pyreon-lint] Watching for changes...\\x1b[0m\\n`)\n\n for (const p of options.paths) {\n const dir = resolve(p)\n try {\n watch(dir, { recursive: true }, (_event, filename) => {\n if (!filename) return\n const filePath = resolve(dir, filename)\n\n if (!hasJsExtension(filePath) || isIgnored(filePath)) return\n\n // Debounce: clear existing timeout for this file\n const existing = pending.get(filePath)\n if (existing) clearTimeout(existing)\n\n pending.set(\n filePath,\n setTimeout(() => {\n pending.delete(filePath)\n relintFile(filePath, config, cache, options.format)\n }, 100),\n )\n })\n } catch {\n console.error(`[pyreon-lint] Could not watch: ${dir}`)\n }\n }\n}\n\nfunction applyOverrides(\n config: LintConfig,\n overrides?: Record<string, Severity> | undefined,\n): void {\n if (!overrides) return\n for (const [id, severity] of Object.entries(overrides)) {\n config.rules[id] = severity\n }\n}\n\nfunction relintFile(filePath: string, config: LintConfig, cache: AstCache, format: string): void {\n let source: string\n try {\n source = readFileSync(filePath, 'utf-8')\n } catch {\n return\n }\n\n const fileResult = lintFile(filePath, source, allRules, config, cache)\n\n if (fileResult.diagnostics.length === 0) return\n\n const result: LintResult = {\n files: [fileResult],\n totalErrors: 0,\n totalWarnings: 0,\n totalInfos: 0,\n }\n\n for (const d of fileResult.diagnostics) {\n if (d.severity === 'error') result.totalErrors++\n else if (d.severity === 'warn') result.totalWarnings++\n else if (d.severity === 'info') result.totalInfos++\n }\n\n // Clear screen and print\n process.stdout.write('\\x1b[2J\\x1b[H')\n console.log(formatOutput(result, format))\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["HOOK_NAME"],"sources":["../src/cache.ts","../src/config/ignore.ts","../src/config/loader.ts","../src/utils/imports.ts","../src/utils/ast.ts","../src/rules/accessibility/dialog-a11y.ts","../src/rules/accessibility/overlay-a11y.ts","../src/rules/accessibility/toast-a11y.ts","../src/utils/exempt-paths.ts","../src/utils/file-roles.ts","../src/rules/architecture/dev-guard-warnings.ts","../src/rules/architecture/no-circular-import.ts","../src/rules/architecture/no-cross-layer-import.ts","../src/rules/architecture/no-deep-import.ts","../src/rules/architecture/no-error-without-prefix.ts","../src/rules/architecture/no-process-dev-gate.ts","../src/rules/architecture/require-browser-smoke-test.ts","../src/rules/form/no-submit-without-validation.ts","../src/rules/form/no-unregistered-field.ts","../src/rules/form/prefer-field-array.ts","../src/rules/hooks/no-raw-addeventlistener.ts","../src/utils/component-context.ts","../src/rules/hooks/no-raw-localstorage.ts","../src/rules/hooks/no-raw-setinterval.ts","../src/rules/jsx/no-and-conditional.ts","../src/rules/jsx/no-children-access.ts","../src/rules/jsx/no-classname.ts","../src/rules/jsx/no-htmlfor.ts","../src/rules/jsx/no-index-as-by.ts","../src/rules/jsx/no-map-in-jsx.ts","../src/rules/jsx/no-missing-for-by.ts","../src/rules/jsx/no-onchange.ts","../src/rules/jsx/no-props-destructure.ts","../src/rules/jsx/no-ternary-conditional.ts","../src/rules/jsx/use-by-not-key.ts","../src/rules/lifecycle/no-dom-in-setup.ts","../src/rules/lifecycle/no-effect-in-mount.ts","../src/rules/lifecycle/no-missing-cleanup.ts","../src/rules/lifecycle/no-mount-in-effect.ts","../src/rules/performance/no-eager-import.ts","../src/rules/performance/no-effect-in-for.ts","../src/rules/performance/no-large-for-without-by.ts","../src/rules/performance/prefer-show-over-display.ts","../src/rules/reactivity/no-bare-signal-in-jsx.ts","../src/rules/reactivity/no-context-destructure.ts","../src/rules/reactivity/no-effect-assignment.ts","../src/rules/reactivity/no-nested-effect.ts","../src/rules/reactivity/no-peek-in-tracked.ts","../src/rules/reactivity/no-signal-in-loop.ts","../src/rules/reactivity/no-signal-in-props.ts","../src/rules/reactivity/no-signal-leak.ts","../src/rules/reactivity/no-unbatched-updates.ts","../src/rules/reactivity/prefer-computed.ts","../src/rules/router/no-href-navigation.ts","../src/rules/router/no-imperative-navigate-in-render.ts","../src/rules/router/no-missing-fallback.ts","../src/rules/router/prefer-use-is-active.ts","../src/rules/ssr/no-mismatch-risk.ts","../src/rules/ssr/no-window-in-ssr.ts","../src/rules/ssr/prefer-request-context.ts","../src/rules/store/no-duplicate-store-id.ts","../src/rules/store/no-mutate-store-state.ts","../src/rules/store/no-store-outside-provider.ts","../src/rules/styling/no-dynamic-styled.ts","../src/rules/styling/no-inline-style-object.ts","../src/rules/styling/no-theme-outside-provider.ts","../src/rules/styling/prefer-cx.ts","../src/rules/index.ts","../src/config/presets.ts","../src/utils/source.ts","../src/utils/index.ts","../src/utils/validate-options.ts","../src/runner.ts","../src/lint.ts","../src/reporter.ts","../src/lsp/index.ts","../src/watcher.ts"],"sourcesContent":["import type { LineIndex } from './utils/source'\n\n/**\n * Simple in-memory cache for parsed ASTs keyed by file content hash.\n *\n * Uses FNV-1a hash of source text as cache key for fast lookups\n * during repeat runs (e.g., watch mode).\n *\n * @example\n * ```ts\n * import { AstCache } from \"@pyreon/lint\"\n *\n * const cache = new AstCache()\n * const cached = cache.get(sourceText)\n * if (!cached) {\n * const parsed = parse(sourceText)\n * cache.set(sourceText, parsed)\n * }\n * ```\n */\nexport class AstCache {\n private cache = new Map<string, { program: any; lineIndex: LineIndex }>()\n\n get(sourceText: string): { program: any; lineIndex: LineIndex } | undefined {\n const key = fnv1aHash(sourceText)\n return this.cache.get(key)\n }\n\n set(sourceText: string, value: { program: any; lineIndex: LineIndex }): void {\n const key = fnv1aHash(sourceText)\n this.cache.set(key, value)\n }\n\n clear(): void {\n this.cache.clear()\n }\n\n get size(): number {\n return this.cache.size\n }\n}\n\n/** FNV-1a hash — fast, non-cryptographic, low collision rate. */\nfunction fnv1aHash(str: string): string {\n let hash = 0x811c9dc5 // FNV offset basis\n for (let i = 0; i < str.length; i++) {\n hash ^= str.charCodeAt(i)\n hash = (hash * 0x01000193) | 0 // FNV prime, keep 32-bit\n }\n return (hash >>> 0).toString(36)\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { join, relative, resolve } from 'node:path'\n\n/**\n * Create a filter function that returns true if a file path should be ignored.\n *\n * Loads patterns from `.pyreonlintignore` and `.gitignore` in the given directory.\n *\n * @example\n * ```ts\n * import { createIgnoreFilter } from \"@pyreon/lint\"\n *\n * const isIgnored = createIgnoreFilter(process.cwd())\n * if (!isIgnored(\"src/app.tsx\")) lintFile(...)\n * ```\n */\nexport function createIgnoreFilter(\n cwd: string,\n extraIgnore?: string | undefined,\n): (filePath: string) => boolean {\n const patterns: string[] = []\n const resolvedCwd = resolve(cwd)\n\n // Load .pyreonlintignore\n loadPatternsFromFile(join(resolvedCwd, '.pyreonlintignore'), patterns)\n\n // Load .gitignore\n loadPatternsFromFile(join(resolvedCwd, '.gitignore'), patterns)\n\n // Load extra ignore file if provided\n if (extraIgnore) {\n loadPatternsFromFile(resolve(extraIgnore), patterns)\n }\n\n // Compile patterns into matchers\n const matchers = patterns.map((p) => compileMatcher(p))\n\n return (filePath: string): boolean => {\n const rel = relative(resolvedCwd, resolve(filePath))\n // Normalize to forward slashes\n const normalized = rel.replace(/\\\\/g, '/')\n\n for (const matcher of matchers) {\n if (matcher(normalized)) return true\n }\n return false\n }\n}\n\nfunction loadPatternsFromFile(filePath: string, patterns: string[]): void {\n if (!existsSync(filePath)) return\n try {\n const content = readFileSync(filePath, 'utf-8')\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n // Skip empty lines and comments\n if (!trimmed || trimmed.startsWith('#')) continue\n patterns.push(trimmed)\n }\n } catch {\n // Ignore read errors\n }\n}\n\n/**\n * Compile a gitignore-style pattern into a matcher function.\n * Supports: `*` (any non-slash chars), `**` (any path segment), `?` (single char),\n * leading `/` (root-anchored), trailing `/` (directory only).\n */\nfunction compileMatcher(pattern: string): (path: string) => boolean {\n let p = pattern\n let anchored = false\n\n // Negated patterns (not supported — just skip them)\n if (p.startsWith('!')) {\n return () => false\n }\n\n // Leading slash means anchored to root\n if (p.startsWith('/')) {\n anchored = true\n p = p.slice(1)\n }\n\n // Trailing slash means only match directories (we treat all paths as files, so strip it\n // and match as a prefix)\n let dirOnly = false\n if (p.endsWith('/')) {\n dirOnly = true\n p = p.slice(0, -1)\n }\n\n const regex = globToRegex(p)\n\n return (path: string): boolean => {\n if (dirOnly) {\n // Match as prefix: the pattern should match a directory portion\n if (anchored) {\n return regex.test(path) || path.startsWith(`${p}/`) || path === p\n }\n // Unanchored directory pattern — match anywhere in path\n return regex.test(path) || path.includes(`/${p}/`) || path.startsWith(`${p}/`) || path === p\n }\n\n if (anchored) {\n return regex.test(path)\n }\n\n // Unanchored pattern — try matching the full path, or just the basename\n if (regex.test(path)) return true\n\n // Also try matching against just the filename\n const lastSlash = path.lastIndexOf('/')\n if (lastSlash !== -1) {\n const basename = path.slice(lastSlash + 1)\n return regex.test(basename)\n }\n\n return false\n }\n}\n\nconst GLOB_CHAR_MAP: Record<string, string> = {\n '?': '[^/]',\n '.': '\\\\.',\n '/': '/',\n}\n\nfunction handleStar(glob: string, pos: number): { pattern: string; advance: number } {\n if (glob[pos + 1] === '*') {\n if (glob[pos + 2] === '/') return { pattern: '(?:.*/)?', advance: 3 }\n return { pattern: '.*', advance: 2 }\n }\n return { pattern: '[^/]*', advance: 1 }\n}\n\nfunction globToRegex(glob: string): RegExp {\n let result = '^'\n let i = 0\n\n while (i < glob.length) {\n const ch = glob[i] as string\n if (ch === '*') {\n const star = handleStar(glob, i)\n result += star.pattern\n i += star.advance\n } else {\n result += GLOB_CHAR_MAP[ch] ?? escapeRegex(ch)\n i++\n }\n }\n\n result += '$'\n return new RegExp(result)\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[\\\\^$+{}[\\]|()]/g, '\\\\$&')\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport type { LintConfigFile } from '../types'\n\nconst CONFIG_FILENAMES = ['.pyreonlintrc.json', '.pyreonlintrc', 'pyreonlint.config.json']\n\n/**\n * Load a lint config file from the given directory or its parents.\n *\n * Search order:\n * 1. `.pyreonlintrc.json`\n * 2. `.pyreonlintrc`\n * 3. `pyreonlint.config.json`\n * 4. `package.json` `\"pyreonlint\"` field\n *\n * @example\n * ```ts\n * import { loadConfig } from \"@pyreon/lint\"\n *\n * const config = loadConfig(process.cwd())\n * if (config) console.log(config.preset)\n * ```\n */\nexport function loadConfig(cwd: string): LintConfigFile | null {\n let dir = resolve(cwd)\n const root = dirname(dir)\n\n while (true) {\n const found = searchDirectory(dir)\n if (found !== null) return found\n\n const parent = dirname(dir)\n if (parent === dir || parent === root) break\n dir = parent\n }\n\n return null\n}\n\nfunction searchDirectory(dir: string): LintConfigFile | null {\n for (const filename of CONFIG_FILENAMES) {\n const content = tryReadJson(join(dir, filename))\n if (content !== null) return content\n }\n return extractPkgConfig(join(dir, 'package.json'))\n}\n\nfunction extractPkgConfig(pkgPath: string): LintConfigFile | null {\n const pkg = tryReadJson(pkgPath)\n if (pkg === null || typeof pkg !== 'object' || !('pyreonlint' in pkg)) return null\n const field = (pkg as Record<string, unknown>).pyreonlint\n if (field && typeof field === 'object') return field as LintConfigFile\n return null\n}\n\n/**\n * Load a config file from a specific path.\n */\nexport function loadConfigFromPath(filePath: string): LintConfigFile | null {\n return tryReadJson(resolve(filePath))\n}\n\nfunction tryReadJson(filePath: string): any | null {\n if (!existsSync(filePath)) return null\n try {\n const raw = readFileSync(filePath, 'utf-8').trim()\n if (!raw) return null\n return JSON.parse(raw)\n } catch {\n return null\n }\n}\n","import type { ImportInfo } from '../types'\n\nexport type { ImportInfo }\n\n// ── Constants ───────────────────────────────────────────────────────────────\n\nexport const PYREON_PREFIX = '@pyreon/'\n\nexport const REACTIVITY_APIS = new Set([\n 'signal',\n 'computed',\n 'effect',\n 'batch',\n 'onCleanup',\n 'createSelector',\n 'createStore',\n 'untrack',\n])\n\nexport const LIFECYCLE_APIS = new Set(['onMount', 'onUnmount'])\n\nexport const CONTEXT_APIS = new Set(['createContext', 'provide', 'pushContext', 'popContext'])\n\nexport const JSX_COMPONENTS = new Set([\n 'For',\n 'Show',\n 'Switch',\n 'Match',\n 'Dynamic',\n 'ErrorBoundary',\n 'Suspense',\n 'Portal',\n])\n\nexport const HEAVY_PACKAGES = new Set([\n '@pyreon/charts',\n '@pyreon/code',\n '@pyreon/document',\n '@pyreon/flow',\n])\n\nexport const BROWSER_GLOBALS = new Set([\n 'window',\n 'document',\n 'navigator',\n 'location',\n 'history',\n 'localStorage',\n 'sessionStorage',\n 'indexedDB',\n // NOTE: `fetch` is intentionally OMITTED — it's a universal global in\n // Node 18+, Bun, Deno, browsers, and edge runtimes. Code using it isn't\n // browser-specific. (`XMLHttpRequest`/`WebSocket` are still here because\n // they're DOM-only and Node code uses different libraries.)\n 'XMLHttpRequest',\n 'WebSocket',\n 'requestAnimationFrame',\n 'cancelAnimationFrame',\n 'IntersectionObserver',\n 'MutationObserver',\n 'ResizeObserver',\n 'matchMedia',\n 'getComputedStyle',\n 'addEventListener',\n 'removeEventListener',\n])\n\n// ── Functions ───────────────────────────────────────────────────────────────\n\nexport function isPyreonImport(source: string): boolean {\n return source.startsWith(PYREON_PREFIX)\n}\n\nexport function isPyreonPackage(source: string): boolean {\n return source.startsWith(PYREON_PREFIX)\n}\n\nexport function extractImportInfo(node: any): ImportInfo | null {\n if (node.type !== 'ImportDeclaration') return null\n\n const source = node.source?.value as string\n if (!source) return null\n\n const specifiers: ImportInfo['specifiers'] = []\n let isDefault = false\n let isNamespace = false\n\n for (const spec of node.specifiers ?? []) {\n if (spec.type === 'ImportDefaultSpecifier') {\n isDefault = true\n specifiers.push({ imported: 'default', local: spec.local?.name ?? '' })\n } else if (spec.type === 'ImportNamespaceSpecifier') {\n isNamespace = true\n specifiers.push({ imported: '*', local: spec.local?.name ?? '' })\n } else if (spec.type === 'ImportSpecifier') {\n const imported =\n spec.imported?.type === 'Identifier' ? spec.imported.name : (spec.imported?.value ?? '')\n specifiers.push({ imported, local: spec.local?.name ?? '' })\n }\n }\n\n return { source, specifiers, isDefault, isNamespace }\n}\n\nexport function importsName(imports: ImportInfo[], name: string, fromPackage?: string): boolean {\n return imports.some(\n (imp) =>\n (!fromPackage || imp.source === fromPackage) &&\n imp.specifiers.some((s) => s.imported === name),\n )\n}\n\nexport function getLocalName(\n imports: ImportInfo[],\n name: string,\n fromPackage?: string,\n): string | null {\n for (const imp of imports) {\n if (fromPackage && imp.source !== fromPackage) continue\n for (const s of imp.specifiers) {\n if (s.imported === name) return s.local\n }\n }\n return null\n}\n","import type { Span } from '../types'\nimport { BROWSER_GLOBALS } from './imports'\n\n/** Check if a node is a call expression to a specific function name. */\nexport function isCallTo(node: any, name: string): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'Identifier' &&\n node.callee.name === name\n )\n}\n\n/** Check if a node is a call expression to any of the given function names. */\nexport function isCallToAny(node: any, names: Set<string>): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'Identifier' &&\n names.has(node.callee.name)\n )\n}\n\n/** Check if a node is a member call like `obj.method()`. */\nexport function isMemberCallTo(node: any, objectName: string, methodName: string): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'MemberExpression' &&\n node.callee.object?.type === 'Identifier' &&\n node.callee.object.name === objectName &&\n node.callee.property?.type === 'Identifier' &&\n node.callee.property.name === methodName\n )\n}\n\n/** Check if a node is a JSX element (opening or self-closing). */\nexport function isJSXElement(node: any): boolean {\n return node.type === 'JSXElement' || node.type === 'JSXFragment'\n}\n\n/** Get the tag name of a JSX element. */\nexport function getJSXTagName(node: any): string | null {\n if (node.type === 'JSXElement') {\n const opening = node.openingElement\n if (!opening) return null\n const name = opening.name\n if (name?.type === 'JSXIdentifier') return name.name\n if (name?.type === 'JSXMemberExpression') {\n return `${name.object?.name ?? ''}.${name.property?.name ?? ''}`\n }\n }\n return null\n}\n\n/** Get a JSX attribute by name from an opening element. */\nexport function getJSXAttribute(openingElement: any, attrName: string): any | null {\n const attrs = openingElement.attributes ?? []\n for (const attr of attrs) {\n if (\n attr.type === 'JSXAttribute' &&\n attr.name?.type === 'JSXIdentifier' &&\n attr.name.name === attrName\n ) {\n return attr\n }\n }\n return null\n}\n\n/** Check if a JSX opening element has an attribute. */\nexport function hasJSXAttribute(openingElement: any, attrName: string): boolean {\n return getJSXAttribute(openingElement, attrName) !== null\n}\n\n/** Check if a node is inside a function (arrow or regular). */\nexport function isInsideFunction(ancestors: any[]): boolean {\n return ancestors.some(\n (a) =>\n a.type === 'FunctionDeclaration' ||\n a.type === 'FunctionExpression' ||\n a.type === 'ArrowFunctionExpression',\n )\n}\n\n/** Check if a node is inside JSX. */\nexport function isInsideJSX(ancestors: any[]): boolean {\n return ancestors.some((a) => a.type === 'JSXElement' || a.type === 'JSXFragment')\n}\n\n/** Check if a node is an array .map() call. */\nexport function isArrayMapCall(node: any): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'MemberExpression' &&\n node.callee.property?.type === 'Identifier' &&\n node.callee.property.name === 'map'\n )\n}\n\n/** Check if a node is a function expression or arrow function. */\nexport function isFunction(node: any): boolean {\n return (\n node.type === 'FunctionDeclaration' ||\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n )\n}\n\n/** Check if a node is a destructuring pattern. */\nexport function isDestructuring(node: any): boolean {\n return node.type === 'ObjectPattern' || node.type === 'ArrayPattern'\n}\n\n/** Check if a node is a ternary with JSX in either branch. */\nexport function isTernaryWithJSX(node: any): boolean {\n if (node.type !== 'ConditionalExpression') return false\n return containsJSX(node.consequent) || containsJSX(node.alternate)\n}\n\n/** Check if a node contains JSX anywhere. */\nfunction containsJSX(node: any): boolean {\n if (!node) return false\n if (node.type === 'JSXElement' || node.type === 'JSXFragment') return true\n if (node.type === 'ParenthesizedExpression') return containsJSX(node.expression)\n return false\n}\n\n/** Check if a JSX element has JSX children. */\nexport function hasJSXChild(node: any): boolean {\n if (node.type !== 'JSXElement') return false\n return (node.children ?? []).some((c: any) => c.type === 'JSXElement' || c.type === 'JSXFragment')\n}\n\n/** Check if a node is a logical AND with JSX. */\nexport function isLogicalAndWithJSX(node: any): boolean {\n if (node.type !== 'LogicalExpression' || node.operator !== '&&') return false\n return containsJSX(node.right)\n}\n\n/** Check if a node is a .peek() call. */\nexport function isPeekCall(node: any): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'MemberExpression' &&\n node.callee.property?.type === 'Identifier' &&\n node.callee.property.name === 'peek'\n )\n}\n\n/** Check if a node is a .set() call. */\nexport function isSetCall(node: any): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'MemberExpression' &&\n node.callee.property?.type === 'Identifier' &&\n node.callee.property.name === 'set'\n )\n}\n\n/** Check if a node references a browser global. */\nexport function isBrowserGlobal(node: any): boolean {\n return node.type === 'Identifier' && BROWSER_GLOBALS.has(node.name)\n}\n\n/** Get the span (byte offsets) of a node. */\nexport function getSpan(node: any): Span {\n return { start: node.start as number, end: node.end as number }\n}\n\n/** Check if a node is inside a `if (__DEV__)` guard. */\nexport function isInsideDevGuard(ancestors: any[]): boolean {\n return ancestors.some(\n (a) => a.type === 'IfStatement' && a.test?.type === 'Identifier' && a.test.name === '__DEV__',\n )\n}\n\n/** Check if a node is inside an onMount callback. */\nexport function isInsideOnMount(ancestors: any[]): boolean {\n return ancestors.some(\n (a) =>\n a.type === 'CallExpression' && a.callee?.type === 'Identifier' && a.callee.name === 'onMount',\n )\n}\n\n/** Check if a node is inside a typeof guard (e.g., `typeof window !== \"undefined\"`). */\nexport function isInsideTypeofGuard(ancestors: any[]): boolean {\n return ancestors.some(\n (a) =>\n a.type === 'IfStatement' &&\n a.test?.type === 'BinaryExpression' &&\n a.test.left?.type === 'UnaryExpression' &&\n a.test.left.operator === 'typeof',\n )\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const dialogA11y: Rule = {\n meta: {\n id: 'pyreon/dialog-a11y',\n category: 'accessibility',\n description: 'Warn when <dialog> is missing aria-label or aria-labelledby.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'dialog') return\n\n const hasLabel = hasJSXAttribute(node, 'aria-label')\n const hasLabelledBy = hasJSXAttribute(node, 'aria-labelledby')\n\n if (!hasLabel && !hasLabelledBy) {\n context.report({\n message:\n '`<dialog>` missing `aria-label` or `aria-labelledby` — provide an accessible label for screen readers.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const overlayA11y: Rule = {\n meta: {\n id: 'pyreon/overlay-a11y',\n category: 'accessibility',\n description: 'Warn when <Overlay> is missing role, aria-label, or aria-labelledby.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'Overlay') return\n\n const hasRole = hasJSXAttribute(node, 'role')\n const hasLabel = hasJSXAttribute(node, 'aria-label')\n const hasLabelledBy = hasJSXAttribute(node, 'aria-labelledby')\n\n if (!hasRole && !hasLabel && !hasLabelledBy) {\n context.report({\n message:\n '`<Overlay>` missing `role`, `aria-label`, or `aria-labelledby` — provide accessibility attributes for screen readers.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const toastA11y: Rule = {\n meta: {\n id: 'pyreon/toast-a11y',\n category: 'accessibility',\n description: 'Warn when toast-like components are missing role or aria-live attributes.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier') return\n\n const tagName: string = name.name\n // Skip non-PascalCase and the Toaster container itself\n if (tagName === 'Toaster') return\n const firstChar = tagName[0]\n if (!firstChar || firstChar !== firstChar.toUpperCase()) return\n if (!tagName.toLowerCase().includes('toast')) return\n\n const hasRole = hasJSXAttribute(node, 'role')\n const hasAriaLive = hasJSXAttribute(node, 'aria-live')\n\n if (!hasRole && !hasAriaLive) {\n context.report({\n message: `Toast component \\`<${tagName}>\\` missing \\`role\\` or \\`aria-live\\` — add \\`role=\"alert\"\\` and \\`aria-live=\"polite\"\\` for screen reader accessibility.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","/**\n * Helper for rules that support path-based exemption via options.\n *\n * Rules that need to be \"turn-off-able for specific directories\" (e.g.\n * a package that IS the foundation the rule recommends against using\n * directly) don't hardcode the paths anymore — they read an\n * `exemptPaths: string[]` option from the user's config:\n *\n * ```json\n * // .pyreonlintrc.json\n * {\n * \"rules\": {\n * \"pyreon/no-window-in-ssr\": [\n * \"error\",\n * { \"exemptPaths\": [\"packages/core/runtime-dom/\"] }\n * ]\n * }\n * }\n * ```\n *\n * Each entry is substring-matched against the file path (same convention\n * the old hardcoded patterns used). Empty / missing → no exemptions,\n * which is the correct default for a rule shipping to user apps.\n */\n\nimport type { RuleContext } from '../types'\n\nexport function isPathExempt(ctx: RuleContext): boolean {\n const options = ctx.getOptions()\n const raw = options.exemptPaths\n if (!Array.isArray(raw) || raw.length === 0) return false\n const filePath = ctx.getFilePath()\n for (const entry of raw) {\n if (typeof entry === 'string' && entry.length > 0 && filePath.includes(entry)) {\n return true\n }\n }\n return false\n}\n","/**\n * Universal file-path classifiers for lint rules.\n *\n * What belongs here:\n * - Conventions that exist in every project the linter runs on\n * (test files, example directories — the `*.test.*` convention\n * is not Pyreon-specific).\n *\n * What does NOT belong here:\n * - Monorepo-specific paths like `packages/core/runtime-dom/` —\n * those are implementation knowledge of one particular codebase\n * and have no meaning in a user's app. Exemptions for such paths\n * belong in the consuming project's lint config via the\n * `exemptPaths: string[]` rule option — see `utils/exempt-paths.ts`\n * and the Pyreon monorepo's `.pyreonlintrc.json` at repo root for\n * reference.\n */\n\n/**\n * Matches files that are tests by convention. Universal — the\n * `*.test.*` / `*.spec.*` / `/tests/` / `/__tests__/` conventions\n * exist in every codebase this linter runs on, not just Pyreon.\n */\nexport function isTestFile(filePath: string): boolean {\n return (\n filePath.includes('/tests/') ||\n filePath.includes('/test/') ||\n filePath.includes('/__tests__/') ||\n filePath.includes('.test.') ||\n filePath.includes('.spec.')\n )\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { isPathExempt } from '../../utils/exempt-paths'\nimport { isTestFile } from '../../utils/file-roles'\n\nexport const devGuardWarnings: Rule = {\n meta: {\n id: 'pyreon/dev-guard-warnings',\n category: 'architecture',\n description: 'Require console.warn/error calls to be wrapped in `if (__DEV__)` guards.',\n severity: 'error',\n fixable: false,\n schema: { exemptPaths: 'string[]', devFlagNames: 'string[]' },\n },\n create(context) {\n // Skip test files — universal convention (`*.test.*` etc. exist in\n // every project this linter runs on and don't ship to production).\n if (isTestFile(context.getFilePath())) return {}\n\n // Configurable `exemptPaths` — projects opt out directories where the\n // rule's premise doesn't apply (server-only code where dev/prod is\n // `process.env.NODE_ENV`, or example / demo directories that ship\n // as documentation rather than production).\n if (isPathExempt(context)) return {}\n\n // Project-level additions to the built-in dev-flag name list. Merged\n // with the defaults so a custom flag like `__DEBUG__` still picks up\n // the built-in `__DEV__`/`IS_DEVELOPMENT`/etc. without restating them.\n const userFlagNames = context.getOptions().devFlagNames\n const extraFlagNames = Array.isArray(userFlagNames)\n ? userFlagNames.filter((n): n is string => typeof n === 'string')\n : []\n\n // Identifiers bound via `const X = <devFlag expression>` — e.g.\n // `const IS_DEVELOPMENT = import.meta.env.DEV === true`. These act as\n // the same dev-mode gate as the raw flag at their call sites.\n const devFlagBoundConsts = new Set<string>()\n function exprResolvesToDevFlag(expr: any): boolean {\n if (!expr) return false\n if (expr.type === 'ChainExpression') return exprResolvesToDevFlag(expr.expression)\n if (isDevFlag(expr)) return true\n // `import.meta.env.DEV === true` / `true === import.meta.env.DEV`\n if (\n expr.type === 'BinaryExpression' &&\n (expr.operator === '===' || expr.operator === '==')\n ) {\n return exprResolvesToDevFlag(expr.left) || exprResolvesToDevFlag(expr.right)\n }\n return false\n }\n\n // Conventional identifier names treated as dev-mode gates. Covers both\n // local `const __DEV__ = …` style and imported flags like `IS_DEV` /\n // `IS_DEVELOPMENT` from a package's shared utils module. The rule can't\n // follow cross-module imports to verify that the binding really resolves\n // to `import.meta.env.DEV`, so we fall back to the name convention —\n // consistent with how the existing `__DEV__` identifier works. Projects\n // can extend the list via the `devFlagNames` rule option.\n const DEV_FLAG_NAMES = new Set<string>([\n '__DEV__',\n 'IS_DEV',\n 'IS_DEVELOPMENT',\n 'isDev',\n ...extraFlagNames,\n ])\n\n // Direct dev-mode flags this rule treats as guards.\n function isDevFlag(node: any): boolean {\n if (!node) return false\n if (node.type === 'ChainExpression') return isDevFlag(node.expression)\n // Conventional dev-flag identifier names.\n if (node.type === 'Identifier' && DEV_FLAG_NAMES.has(node.name)) return true\n // Const-bound dev flag, e.g. `const devMode = import.meta.env.DEV`.\n if (node.type === 'Identifier' && devFlagBoundConsts.has(node.name)) return true\n // `import.meta.env.DEV` (and `import.meta.env?.DEV` after ChainExpression unwrap)\n if (\n node.type === 'MemberExpression' &&\n node.property?.type === 'Identifier' &&\n node.property.name === 'DEV'\n ) {\n const obj = node.object\n if (\n obj?.type === 'MemberExpression' &&\n obj.property?.type === 'Identifier' &&\n obj.property.name === 'env' &&\n obj.object?.type === 'MetaProperty'\n )\n return true\n }\n return false\n }\n\n // Match `<flag>`, `<flag> && X`, `X && <flag>`, `<flag> === true`, etc.\n // `&&` only — `||` doesn't guarantee dev-only execution.\n function containsDevGuard(test: any): boolean {\n if (!test) return false\n if (isDevFlag(test)) return true\n if (test.type === 'LogicalExpression' && test.operator === '&&') {\n return containsDevGuard(test.left) || containsDevGuard(test.right)\n }\n // `flag === true` or `true === flag` — common after `?? === true` shape.\n if (\n test.type === 'BinaryExpression' &&\n (test.operator === '===' || test.operator === '==')\n ) {\n return isDevFlag(test.left) || isDevFlag(test.right)\n }\n return false\n }\n\n // Detects an early-return DEV guard at the head of a function body:\n // `if (!__DEV__) return` / `if (!import.meta.env.DEV) return`\n // Everything after this in the function is implicitly dev-only.\n function isEarlyReturnDevGuard(node: any): boolean {\n if (!node || node.type !== 'IfStatement') return false\n const t = node.test\n const arg = t?.type === 'UnaryExpression' && t.operator === '!' ? t.argument : null\n if (!arg) return false\n if (!isDevFlag(arg)) return false\n const c = node.consequent\n if (c?.type === 'ReturnStatement') return true\n if (c?.type === 'BlockStatement' && c.body.length === 1 && c.body[0]?.type === 'ReturnStatement') return true\n return false\n }\n\n let devGuardDepth = 0\n let catchDepth = 0\n // For each function we enter, record whether its first statement is an\n // early-return DEV guard. If yes, the function's body is dev-only and\n // we treat it as one guard depth for the duration.\n const funcGuardStack: number[] = []\n function enterFunction(node: any) {\n const body = node?.body\n const stmts = body?.type === 'BlockStatement' ? body.body : null\n let guarded = 0\n if (stmts && stmts.length > 0 && isEarlyReturnDevGuard(stmts[0])) {\n guarded = 1\n devGuardDepth++\n }\n funcGuardStack.push(guarded)\n }\n function exitFunction() {\n const g = funcGuardStack.pop() ?? 0\n if (g > 0) devGuardDepth -= g\n }\n\n const callbacks: VisitorCallbacks = {\n VariableDeclaration(node: any) {\n for (const decl of node.declarations ?? []) {\n if (decl.id?.type === 'Identifier' && exprResolvesToDevFlag(decl.init)) {\n devFlagBoundConsts.add(decl.id.name)\n }\n }\n },\n FunctionDeclaration: enterFunction,\n 'FunctionDeclaration:exit': exitFunction,\n FunctionExpression: enterFunction,\n 'FunctionExpression:exit': exitFunction,\n ArrowFunctionExpression: enterFunction,\n 'ArrowFunctionExpression:exit': exitFunction,\n\n IfStatement(node: any) {\n if (containsDevGuard(node.test)) devGuardDepth++\n },\n 'IfStatement:exit'(node: any) {\n if (containsDevGuard(node.test)) devGuardDepth--\n },\n // Conditional expression as a statement — `__DEV__ && console.warn(...)`\n // and `__DEV__ ? console.warn(...) : null` are equivalent dev-only hints.\n LogicalExpression(node: any) {\n if (node.operator === '&&' && containsDevGuard(node.left)) devGuardDepth++\n },\n 'LogicalExpression:exit'(node: any) {\n if (node.operator === '&&' && containsDevGuard(node.left)) devGuardDepth--\n },\n ConditionalExpression(node: any) {\n if (containsDevGuard(node.test)) devGuardDepth++\n },\n 'ConditionalExpression:exit'(node: any) {\n if (containsDevGuard(node.test)) devGuardDepth--\n },\n // `console.error` in a catch block is legitimate production error\n // reporting (the error already happened — surfacing it isn't a dev hint).\n // `console.warn` in catch is still flagged: warnings should be DEV-only.\n CatchClause() {\n catchDepth++\n },\n 'CatchClause:exit'() {\n catchDepth--\n },\n CallExpression(node: any) {\n if (devGuardDepth > 0) return\n\n const callee = node.callee\n if (\n callee?.type === 'MemberExpression' &&\n callee.object?.type === 'Identifier' &&\n callee.object.name === 'console' &&\n callee.property?.type === 'Identifier' &&\n (callee.property.name === 'warn' || callee.property.name === 'error')\n ) {\n if (callee.property.name === 'error' && catchDepth > 0) return\n context.report({\n message: `\\`console.${callee.property.name}()\\` without \\`__DEV__\\` guard — dev warnings must be tree-shakeable in production. Wrap in \\`if (__DEV__) { ... }\\` (or \\`__DEV__ && ...\\`). Production error logging in \\`catch\\` blocks is exempt for \\`console.error\\`.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { isPyreonImport } from '../../utils/imports'\nimport { isTestFile } from '../../utils/file-roles'\n\nconst LAYER_ORDER: Record<string, number> = {\n '@pyreon/reactivity': 0,\n '@pyreon/core': 1,\n '@pyreon/compiler': 1,\n '@pyreon/runtime-dom': 2,\n '@pyreon/runtime-server': 2,\n '@pyreon/router': 3,\n '@pyreon/head': 4,\n '@pyreon/server': 5,\n}\n\nfunction getLayer(source: string): number | null {\n return LAYER_ORDER[source] ?? null\n}\n\nfunction getFileLayer(filePath: string): number | null {\n for (const [pkg, layer] of Object.entries(LAYER_ORDER)) {\n const pkgName = pkg.replace('@pyreon/', '')\n if (filePath.includes(`/packages/core/${pkgName}/`)) return layer\n }\n return null\n}\n\nexport const noCircularImport: Rule = {\n meta: {\n id: 'pyreon/no-circular-import',\n category: 'architecture',\n description: 'Enforce package layer order to prevent circular imports between core packages.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n const filePath = context.getFilePath()\n // Tests don't ship as part of the layered production dep graph — they're\n // verification scaffolding. Cross-layer imports are routine and correct\n // there (e.g. a `runtime-dom` test importing `renderToString` from\n // `runtime-server` to compare SSR vs CSR output). Path-based skip is the\n // semantic truth for this rule, not a heuristic.\n if (isTestFile(filePath)) return {}\n const fileLayer = getFileLayer(filePath)\n if (fileLayer === null) return {}\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const source = node.source?.value as string\n if (!source || !isPyreonImport(source)) return\n\n const importLayer = getLayer(source)\n if (importLayer === null) return\n\n if (importLayer >= fileLayer) {\n context.report({\n message: `Importing \\`${source}\\` (layer ${importLayer}) from layer ${fileLayer} — this violates the package layer order and may cause circular imports.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { isPyreonImport } from '../../utils/imports'\n\ntype PackageCategory = 'core' | 'fundamentals' | 'tools' | 'ui-system'\n\nconst CORE_PACKAGES = new Set([\n '@pyreon/reactivity',\n '@pyreon/core',\n '@pyreon/compiler',\n '@pyreon/runtime-dom',\n '@pyreon/runtime-server',\n '@pyreon/router',\n '@pyreon/head',\n '@pyreon/server',\n])\n\nconst UI_PACKAGES = new Set([\n '@pyreon/ui-core',\n '@pyreon/styler',\n '@pyreon/unistyle',\n '@pyreon/elements',\n '@pyreon/attrs',\n '@pyreon/rocketstyle',\n '@pyreon/coolgrid',\n '@pyreon/kinetic',\n '@pyreon/kinetic-presets',\n '@pyreon/connector-document',\n '@pyreon/document-primitives',\n])\n\nfunction getImportCategory(source: string): PackageCategory | null {\n if (CORE_PACKAGES.has(source)) return 'core'\n if (UI_PACKAGES.has(source)) return 'ui-system'\n return null\n}\n\nfunction getFileCategory(filePath: string): PackageCategory | null {\n if (filePath.includes('/packages/core/')) return 'core'\n if (filePath.includes('/packages/ui-system/')) return 'ui-system'\n if (filePath.includes('/packages/fundamentals/')) return 'fundamentals'\n if (filePath.includes('/packages/tools/')) return 'tools'\n return null\n}\n\nexport const noCrossLayerImport: Rule = {\n meta: {\n id: 'pyreon/no-cross-layer-import',\n category: 'architecture',\n description: 'Prevent core packages from importing ui-system packages.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n const filePath = context.getFilePath()\n const fileCategory = getFileCategory(filePath)\n if (fileCategory !== 'core') return {}\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const source = node.source?.value as string\n if (!source || !isPyreonImport(source)) return\n\n const importCategory = getImportCategory(source)\n if (importCategory === 'ui-system') {\n context.report({\n message: `Core package importing ui-system package \\`${source}\\` — core packages must not depend on ui-system.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { isPyreonImport } from '../../utils/imports'\n\nconst DEEP_IMPORT_PATTERN = /@pyreon\\/[^/]+\\/(src|dist|lib)\\//\n\nexport const noDeepImport: Rule = {\n meta: {\n id: 'pyreon/no-deep-import',\n category: 'architecture',\n description:\n 'Disallow importing from @pyreon/*/src/, /dist/, or /lib/ — use public exports instead.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const source = node.source?.value as string\n if (!source || !isPyreonImport(source)) return\n\n if (DEEP_IMPORT_PATTERN.test(source)) {\n context.report({\n message: `Deep import \\`${source}\\` — import from the package's public exports instead.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noErrorWithoutPrefix: Rule = {\n meta: {\n id: 'pyreon/no-error-without-prefix',\n category: 'architecture',\n description: 'Require error messages to be prefixed with [Pyreon].',\n severity: 'warn',\n fixable: true,\n },\n create(context) {\n const filePath = context.getFilePath()\n // Skip test files\n if (\n filePath.includes('/tests/') ||\n filePath.includes('/test/') ||\n filePath.includes('.test.') ||\n filePath.includes('.spec.')\n ) {\n return {}\n }\n\n const callbacks: VisitorCallbacks = {\n ThrowStatement(node: any) {\n const arg = node.argument\n if (!arg || arg.type !== 'NewExpression') return\n const callee = arg.callee\n if (!callee || callee.type !== 'Identifier' || callee.name !== 'Error') return\n\n const args = arg.arguments\n if (!args || args.length === 0) return\n\n const firstArg = args[0]\n if (!firstArg) return\n\n if (firstArg.type === 'Literal' || firstArg.type === 'StringLiteral') {\n const value = firstArg.value as string\n if (typeof value === 'string' && !value.startsWith('[Pyreon]')) {\n const argSpan = getSpan(firstArg)\n // Fix: add [Pyreon] prefix\n const quote = context.getSourceText()[argSpan.start]\n const fixedValue = `${quote}[Pyreon] ${value}${quote}`\n context.report({\n message:\n 'Error message missing `[Pyreon]` prefix — all framework errors should be prefixed for identification.',\n span: getSpan(node),\n fix: { span: argSpan, replacement: fixedValue },\n })\n }\n }\n\n if (firstArg.type === 'TemplateLiteral') {\n const quasis = firstArg.quasis\n if (quasis && quasis.length > 0) {\n const first = quasis[0]\n const raw = first.value?.raw ?? first.value?.cooked ?? ''\n if (!raw.startsWith('[Pyreon]')) {\n const argSpan = getSpan(firstArg)\n const source = context.getSourceText().slice(argSpan.start, argSpan.end)\n const fixed = source.replace(/^`/, '`[Pyreon] ')\n context.report({\n message:\n 'Error message missing `[Pyreon]` prefix — all framework errors should be prefixed for identification.',\n span: getSpan(node),\n fix: { span: argSpan, replacement: fixed },\n })\n }\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { isPathExempt } from '../../utils/exempt-paths'\nimport { isTestFile } from '../../utils/file-roles'\n\n/**\n * `pyreon/no-process-dev-gate` — flag the broken `typeof process` dev-mode gate\n * pattern that is dead code in real Vite browser bundles.\n *\n * The pattern this rule catches:\n *\n * ```ts\n * const __DEV__ = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'\n * ```\n *\n * This works in vitest (Node, `process` is defined) but is **silently dead\n * code in real Vite browser bundles** because Vite does not polyfill\n * `process` for the client. Every dev warning gated on this constant never\n * fires for real users in dev mode.\n *\n * The fix is to use `import.meta.env.DEV`, which Vite/Rolldown literal-replace\n * at build time:\n *\n * ```ts\n * // No const needed — read directly at the use site so the bundler can fold:\n * if (!import.meta.env?.DEV) return\n * ```\n *\n * Vitest sets `import.meta.env.DEV === true` automatically (because it is\n * Vite-based), so existing tests continue to pass.\n *\n * Reference implementation: `packages/fundamentals/flow/src/layout.ts:warnIgnoredOptions`.\n *\n * **Auto-fix**: replaces the assignment with `import.meta.env?.DEV === true`.\n * Does NOT delete the const declaration — that has to happen by hand because\n * the variable name and downstream usages may need updating in callers.\n *\n * **Server-only exemption**: projects configure `exemptPaths` per-file for\n * server-only code (Node environments where `process` is always defined and\n * the pattern is correct). Configure in `.pyreonlintrc.json`:\n *\n * {\n * \"rules\": {\n * \"pyreon/no-process-dev-gate\": [\n * \"error\",\n * { \"exemptPaths\": [\"packages/zero/\", \"packages/core/server/\"] }\n * ]\n * }\n * }\n */\n\nexport const noProcessDevGate: Rule = {\n meta: {\n id: 'pyreon/no-process-dev-gate',\n category: 'architecture',\n description:\n 'Forbid `typeof process !== \"undefined\" && process.env.NODE_ENV !== \"production\"` as a dev-mode gate. Use `import.meta.env.DEV` instead — `typeof process` is dead code in real Vite browser bundles because Vite does not polyfill `process` for the client.',\n severity: 'error',\n fixable: true,\n },\n create(context) {\n // Skip test files — vitest has `process`, the gate works there, and\n // tests are not shipped to users. Universal, not a heuristic.\n if (isTestFile(context.getFilePath())) return {}\n\n // Configurable `exemptPaths` option for server-only directories.\n if (isPathExempt(context)) return {}\n\n /**\n * Match the broken pattern at the AST level. We're looking for any\n * `LogicalExpression` whose two sides are:\n *\n * 1. `typeof process !== 'undefined'` (a UnaryExpression on the LHS\n * of a BinaryExpression with operator `!==`)\n * 2. `process.env.NODE_ENV !== 'production'` (a MemberExpression on\n * the LHS of a BinaryExpression with operator `!==`)\n *\n * The order can be either way (process check first or NODE_ENV check\n * first), and the operator can be `&&` or `||` (we only flag `&&`\n * because `||` doesn't make sense as a dev gate).\n */\n function isTypeofProcessCheck(node: any): boolean {\n // typeof process !== 'undefined'\n if (node?.type !== 'BinaryExpression') return false\n if (node.operator !== '!==' && node.operator !== '!=') return false\n const left = node.left\n const right = node.right\n if (left?.type !== 'UnaryExpression' || left.operator !== 'typeof') return false\n if (left.argument?.type !== 'Identifier' || left.argument.name !== 'process') return false\n if (\n (right?.type === 'Literal' || right?.type === 'StringLiteral') &&\n right.value === 'undefined'\n ) {\n return true\n }\n return false\n }\n\n function isNodeEnvCheck(node: any): boolean {\n // process.env.NODE_ENV !== 'production'\n if (node?.type !== 'BinaryExpression') return false\n if (node.operator !== '!==' && node.operator !== '!=') return false\n const left = node.left\n const right = node.right\n if (left?.type !== 'MemberExpression') return false\n if (left.object?.type !== 'MemberExpression') return false\n if (left.object.object?.type !== 'Identifier' || left.object.object.name !== 'process') {\n return false\n }\n if (left.object.property?.type !== 'Identifier' || left.object.property.name !== 'env') {\n return false\n }\n if (left.property?.type !== 'Identifier' || left.property.name !== 'NODE_ENV') return false\n if (\n (right?.type === 'Literal' || right?.type === 'StringLiteral') &&\n right.value === 'production'\n ) {\n return true\n }\n return false\n }\n\n function isBrokenDevGate(node: any): boolean {\n if (node?.type !== 'LogicalExpression') return false\n if (node.operator !== '&&') return false\n // Order can be (typeof process) && (NODE_ENV) OR vice versa\n return (\n (isTypeofProcessCheck(node.left) && isNodeEnvCheck(node.right)) ||\n (isNodeEnvCheck(node.left) && isTypeofProcessCheck(node.right))\n )\n }\n\n const callbacks: VisitorCallbacks = {\n LogicalExpression(node: any) {\n if (!isBrokenDevGate(node)) return\n\n const span = getSpan(node)\n // Auto-fix: replace the entire `typeof process ... && process.env.NODE_ENV ...`\n // expression with `import.meta.env?.DEV === true`. We use optional\n // chaining + strict equality so the expression is `false` (not\n // `undefined`) when `import.meta.env` is missing — preserving the\n // boolean shape callers expect.\n const replacement = 'import.meta.env?.DEV === true'\n\n context.report({\n message:\n '`typeof process !== \"undefined\" && process.env.NODE_ENV !== \"production\"` is dead code in real Vite browser bundles — Vite does not polyfill `process`, so this guard is `false` and any wrapped dev warnings never fire for real users. Use `import.meta.env.DEV` instead, which Vite literal-replaces at build time and tree-shakes correctly in prod. Reference implementation: `packages/fundamentals/flow/src/layout.ts:warnIgnoredOptions`.',\n span,\n fix: { span, replacement },\n })\n },\n }\n\n return callbacks\n },\n}\n","import { existsSync, readdirSync, statSync } from 'node:fs'\nimport path from 'node:path'\nimport type { Rule, VisitorCallbacks } from '../../types'\nimport { isPathExempt } from '../../utils/exempt-paths'\n\n/**\n * `pyreon/require-browser-smoke-test` — every browser-categorized package\n * must ship at least one `*.browser.test.{ts,tsx}` file under `src/`.\n *\n * Locks in the durability of the T1.1 browser smoke harness (PRs #224,\n * #227, #229, #231). Without this rule, any new browser-running package\n * can quietly ship without a real-browser smoke test and we drift back\n * to the world before T1.1 — where happy-dom silently masks\n * environment-divergence bugs (PR #197 mock-vnode metadata drop, PR\n * #200 `typeof process` dead code, the multi-word event delegation bug\n * fixed alongside PR #231).\n *\n * **What it checks**: when linting a package's `src/index.ts`, the rule\n * looks at the package directory for any file matching\n * `**\\/*.browser.test.{ts,tsx}`. If none are found AND the package's\n * name appears in the browser-categorized list, the rule reports an\n * error on `src/index.ts`.\n *\n * **Why src/index.ts only**: the rule needs to fire exactly once per\n * package, not per file. `src/index.ts` is a stable per-package entry\n * point. Files inside the package are not browser-test files\n * themselves, so they get skipped via the path check.\n *\n * **Default browser packages list**: matches the categorization in\n * `.claude/rules/test-environment-parity.md`. Override via the\n * `additionalPackages` option to opt in new packages, or via\n * `exemptPaths` to opt out (e.g. for a brand-new package still under\n * construction).\n *\n * @example Configuration in `.pyreonlintrc.json`\n * ```json\n * {\n * \"rules\": {\n * \"pyreon/require-browser-smoke-test\": [\n * \"error\",\n * {\n * \"additionalPackages\": [\"@my-org/my-browser-pkg\"],\n * \"exemptPaths\": [\"packages/experimental/\"]\n * }\n * ]\n * }\n * }\n * ```\n *\n * **Known limitation — file existence, not test quality.** The rule only\n * checks that at least one `*.browser.test.*` file exists under `src/`;\n * it cannot assess whether the test is meaningful. A package could ship\n * `sanity.browser.test.ts` with `expect(1).toBe(1)` and satisfy the\n * rule. That's accepted by design — the rule is a *gate* against\n * packages shipping with zero smoke coverage, not a quality check.\n * Review the actual test contents on PR. If drive-by one-liner tests\n * become a pattern, add a per-package coverage threshold or a\n * complementary rule that inspects test file contents.\n */\n\n/**\n * Single source of truth for browser-categorized packages lives at\n * `.claude/rules/browser-packages.json`. Loading it lazily here means:\n *\n * 1. Updating the list never requires re-publishing `@pyreon/lint`.\n * 2. The script `scripts/check-browser-smoke.ts` + the human-readable\n * `.claude/rules/test-environment-parity.md` share the same source,\n * so they can't drift out of sync silently.\n *\n * The JSON is searched for by walking up from the linted file's directory\n * to the first ancestor containing `.claude/rules/browser-packages.json`.\n * If not found (rule running in a consumer repo that doesn't ship the\n * JSON), the rule falls back to an empty list — `additionalPackages`\n * becomes the only signal and the rule stays opt-in, not a footgun.\n *\n * Cached globally because the list is tiny and lint runs lint thousands\n * of files per invocation.\n */\nlet _cachedBrowserPackages: Set<string> | null = null\n\nfunction loadBrowserPackages(fromFile: string): Set<string> {\n if (_cachedBrowserPackages) return _cachedBrowserPackages\n let dir = path.dirname(fromFile)\n // Walk up to /; bounded in practice by the project root.\n for (let i = 0; i < 30; i++) {\n const candidate = path.join(dir, '.claude', 'rules', 'browser-packages.json')\n if (existsSync(candidate)) {\n try {\n const fs = require('node:fs') as typeof import('node:fs')\n const parsed = JSON.parse(fs.readFileSync(candidate, 'utf8')) as {\n packages?: unknown\n }\n if (Array.isArray(parsed.packages)) {\n _cachedBrowserPackages = new Set(\n parsed.packages.filter((p): p is string => typeof p === 'string'),\n )\n return _cachedBrowserPackages\n }\n } catch {\n // fall through to empty-list fallback\n }\n break\n }\n const parent = path.dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n _cachedBrowserPackages = new Set()\n return _cachedBrowserPackages\n}\n\n/**\n * Test-only: reset the cached list so unit tests can exercise the\n * filesystem-discovery path multiple times within one process.\n */\nexport function _resetBrowserPackagesCache(): void {\n _cachedBrowserPackages = null\n}\n\n/**\n * Walk a directory looking for `*.browser.test.{ts,tsx}` files. Bails\n * on the first match — we only need to know `at least one exists`,\n * not enumerate them. Skips `node_modules`, `lib`, `dist`, and dot\n * directories so a package's own dependencies don't pollute the check.\n */\nfunction hasBrowserTest(dir: string): boolean {\n let entries: string[]\n try {\n entries = readdirSync(dir)\n } catch {\n return false\n }\n for (const name of entries) {\n if (name.startsWith('.') || name === 'node_modules' || name === 'lib' || name === 'dist') {\n continue\n }\n const full = path.join(dir, name)\n let isDir = false\n try {\n isDir = statSync(full).isDirectory()\n } catch {\n continue\n }\n if (isDir) {\n if (hasBrowserTest(full)) return true\n continue\n }\n if (/\\.browser\\.test\\.(?:ts|tsx)$/.test(name)) return true\n }\n return false\n}\n\n/**\n * Read the package.json `name` field for the directory containing the\n * given src/index.ts file. Returns null if not found.\n */\nfunction readPackageName(srcIndexPath: string): string | null {\n // src/index.ts -> ../package.json\n const pkgPath = path.resolve(path.dirname(srcIndexPath), '..', 'package.json')\n if (!existsSync(pkgPath)) return null\n try {\n // Read synchronously; cheap for one file per package per lint run.\n const text = require('node:fs').readFileSync(pkgPath, 'utf8') as string\n const parsed = JSON.parse(text) as { name?: unknown }\n return typeof parsed.name === 'string' ? parsed.name : null\n } catch {\n return null\n }\n}\n\nexport const requireBrowserSmokeTest: Rule = {\n meta: {\n id: 'pyreon/require-browser-smoke-test',\n category: 'architecture',\n description:\n 'Every browser-categorized package must ship at least one `*.browser.test.{ts,tsx}` file under `src/`. Locks in the T1.1 browser smoke harness.',\n severity: 'error',\n fixable: false,\n schema: {\n additionalPackages: 'string[]',\n exemptPaths: 'string[]',\n },\n },\n create(context): VisitorCallbacks {\n const filePath = context.getFilePath()\n\n // Run exactly once per package: only on `<package>/src/index.ts`\n // (or .tsx). Test files in the package are excluded automatically\n // because they don't match this pattern.\n if (\n !filePath.endsWith('/src/index.ts') &&\n !filePath.endsWith('/src/index.tsx')\n ) {\n return {}\n }\n\n if (isPathExempt(context)) return {}\n\n const pkgName = readPackageName(filePath)\n if (pkgName == null) return {}\n\n const options = context.getOptions()\n const additional = Array.isArray(options.additionalPackages)\n ? (options.additionalPackages.filter((s) => typeof s === 'string') as string[])\n : []\n const browserPackages = new Set(loadBrowserPackages(filePath))\n for (const p of additional) browserPackages.add(p)\n\n if (!browserPackages.has(pkgName)) return {}\n\n const pkgDir = path.dirname(path.dirname(filePath)) // strip /src/index.ts\n if (hasBrowserTest(pkgDir)) return {}\n\n return {\n 'Program:exit'(node: { start?: number; end?: number }) {\n context.report({\n message:\n `[Pyreon] Browser-categorized package \"${pkgName}\" has no \\`*.browser.test.{ts,tsx}\\` file. ` +\n `Add at least one real-browser smoke test under \\`src/\\` to catch environment-divergence bugs ` +\n `that happy-dom hides (typeof process dead code, real pointer events, computed styles, etc.). ` +\n `See .claude/rules/test-environment-parity.md for the recipe.`,\n span: { start: node.start ?? 0, end: node.end ?? 0 },\n })\n },\n }\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\nimport { isTestFile } from '../../utils/file-roles'\n\nexport const noSubmitWithoutValidation: Rule = {\n meta: {\n id: 'pyreon/no-submit-without-validation',\n category: 'form',\n description: 'Warn when useForm() has onSubmit but no validators or schema.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n // Heuristic: skip test files. The rule fires on any `useForm({ onSubmit })`\n // missing validators, but tests deliberately exercise the un-validated\n // path. A truly precise check would need to detect \"this `useForm` is\n // a test stub vs a real production form\" — impractical at lint level.\n // Keep the heuristic; consumers who want to test forms with validation\n // explicitly opted-out should use `// pyreon-lint-disable-next-line`.\n if (isTestFile(context.getFilePath())) return {}\n\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'useForm')) return\n const args = node.arguments\n if (!args || args.length === 0) return\n\n const options = args[0]\n if (!options || options.type !== 'ObjectExpression') return\n\n let hasOnSubmit = false\n let hasValidation = false\n\n for (const prop of options.properties ?? []) {\n if (prop.type !== 'Property') continue\n const key = prop.key\n if (!key) continue\n const name = key.type === 'Identifier' ? key.name : null\n if (name === 'onSubmit') hasOnSubmit = true\n if (name === 'validators' || name === 'schema') hasValidation = true\n }\n\n if (hasOnSubmit && !hasValidation) {\n context.report({\n message:\n '`useForm()` has `onSubmit` without `validators` or `schema` — consider adding validation for data integrity.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\nimport { isTestFile } from '../../utils/file-roles'\n\nexport const noUnregisteredField: Rule = {\n meta: {\n id: 'pyreon/no-unregistered-field',\n category: 'form',\n description: 'Warn when useField() is called without a corresponding register() call.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n // Heuristic: skip test files. The rule fires when `useField()` is\n // called but no matching `register()` is found — usually a real bug\n // (the field is dead). But form tests routinely call `useField` to\n // assert field state without rendering a real DOM input. A precise\n // check would need to detect \"the return is not destructured into\n // props passed to a JSX element\" — impractical at lint level.\n if (isTestFile(context.getFilePath())) return {}\n\n const fieldDecls = new Map<string, { span: { start: number; end: number } }>()\n const registeredNames = new Set<string>()\n\n const callbacks: VisitorCallbacks = {\n VariableDeclarator(node: any) {\n const init = node.init\n if (!init || !isCallTo(init, 'useField')) return\n const id = node.id\n if (!id || id.type !== 'Identifier') return\n fieldDecls.set(id.name, { span: getSpan(node) })\n },\n CallExpression(node: any) {\n const callee = node.callee\n if (!callee || callee.type !== 'MemberExpression') return\n if (callee.property?.type !== 'Identifier' || callee.property.name !== 'register') return\n if (callee.object?.type === 'Identifier') {\n registeredNames.add(callee.object.name)\n }\n },\n 'Program:exit'() {\n for (const [name, { span }] of fieldDecls) {\n if (!registeredNames.has(name)) {\n context.report({\n message: `\\`useField()\\` result \\`${name}\\` is never registered — call \\`${name}.register()\\` to connect it to the form.`,\n span,\n })\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\nimport { extractImportInfo } from '../../utils/imports'\n\nexport const preferFieldArray: Rule = {\n meta: {\n id: 'pyreon/prefer-field-array',\n category: 'form',\n description: 'Suggest useFieldArray() instead of signal([]) in files that import @pyreon/form.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n let importsForm = false\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (info && info.source === '@pyreon/form') {\n importsForm = true\n }\n },\n CallExpression(node: any) {\n if (!importsForm) return\n if (!isCallTo(node, 'signal')) return\n\n const args = node.arguments\n if (!args || args.length === 0) return\n const firstArg = args[0]\n if (firstArg?.type === 'ArrayExpression') {\n context.report({\n message:\n '`signal([])` in a form file — consider using `useFieldArray()` for dynamic array fields with stable keys.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { isPathExempt } from '../../utils/exempt-paths'\n\nexport const noRawAddEventListener: Rule = {\n meta: {\n id: 'pyreon/no-raw-addeventlistener',\n category: 'hooks',\n description: 'Suggest useEventListener() instead of raw .addEventListener() calls.',\n severity: 'info',\n fixable: false,\n schema: { exemptPaths: 'string[]' },\n },\n create(context) {\n // Configurable `exemptPaths` — for packages that IMPLEMENT the cleanup\n // wrapper this rule recommends (they can't use themselves). Configure\n // per-project; user apps typically leave empty.\n if (isPathExempt(context)) return {}\n\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n const callee = node.callee\n if (!callee || callee.type !== 'MemberExpression') return\n if (callee.property?.type !== 'Identifier' || callee.property.name !== 'addEventListener')\n return\n context.report({\n message:\n 'Raw `.addEventListener()` — consider using `useEventListener()` from `@pyreon/hooks` for auto-cleanup on unmount.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","/**\n * Component / hook scope tracker for lint rules.\n *\n * Many rules in this package only matter *inside* a component or hook —\n * e.g. `no-raw-setinterval` wants you to wrap timers in `onMount` so they\n * are cleaned up when the component unmounts. A `setInterval` at module\n * scope, in a utility function, or inside a test callback has its own\n * lifecycle and doesn't need component-tied cleanup.\n *\n * Previously these rules used a path-string `isTestFile()` skip as a\n * proxy for \"outside component context\". That's a heuristic — it\n * accidentally exempts test files where the pattern *is* still wrong,\n * and it accidentally fires on utility/library files where the pattern\n * is fine.\n *\n * This tracker maintains a \"component depth\" counter via visitor\n * callbacks. A function counts as a component or hook when its name\n * follows the framework's naming conventions:\n * - `MyThing` (PascalCase) → component\n * - `useThing` (camelCase, `use` prefix + uppercase next char) → hook\n *\n * Rules consume it via `createComponentContextTracker()` and merge the\n * returned `callbacks` into their visitor:\n *\n * ```ts\n * create(context) {\n * const ctx = createComponentContextTracker()\n * return {\n * ...ctx.callbacks,\n * CallExpression(node) {\n * if (!ctx.isInComponentOrHook()) return\n * // ... existing rule logic ...\n * },\n * }\n * }\n * ```\n *\n * The tracker also recognizes named arrow / function expressions assigned\n * to `const X = (props) => …`, since the framework treats those as\n * components too. Anonymous callbacks (e.g. `it('...', () => { … })`,\n * `setTimeout(() => { … }, 0)`, `arr.map(x => x)`) never push depth — so\n * test bodies, IIFEs, and inline callbacks are correctly seen as\n * \"outside any component\" without needing a path-based skip.\n */\n\nimport type { VisitorCallbacks } from '../types'\n\nconst COMPONENT_NAME = /^[A-Z]/\nconst HOOK_NAME = /^use[A-Z]/\n\nexport function isComponentOrHookName(name: string | null | undefined): boolean {\n if (!name) return false\n return COMPONENT_NAME.test(name) || HOOK_NAME.test(name)\n}\n\nexport interface ComponentContextTracker {\n /** True iff the current AST position is inside a function recognised as a component or hook. */\n isInComponentOrHook(): boolean\n /**\n * Visitor callbacks that maintain the depth counter. Spread them into the\n * rule's returned visitor first; per-node listeners after override only\n * the keys the rule itself implements (FunctionDeclaration etc. rarely).\n */\n callbacks: VisitorCallbacks\n}\n\nexport function createComponentContextTracker(): ComponentContextTracker {\n let depth = 0\n\n // For arrow / function expressions assigned to a `const X = (...) => …`,\n // we can't read the binding name from the function node — and the oxc\n // visitor doesn't pass `parent` to callbacks. Instead, hook the parent\n // `VariableDeclarator` enter/exit: it visits BEFORE its `init` child\n // (the function expression) and EXITS AFTER, so a depth bump tied to\n // the declarator correctly brackets the function body.\n function declaratorIsComponentOrHook(node: any): boolean {\n if (node?.id?.type !== 'Identifier') return false\n const init = node.init\n if (\n init?.type !== 'ArrowFunctionExpression' &&\n init?.type !== 'FunctionExpression'\n )\n return false\n return isComponentOrHookName(node.id.name)\n }\n\n return {\n isInComponentOrHook: () => depth > 0,\n callbacks: {\n // function MyComp() {} / function useFoo() {}\n FunctionDeclaration(node: any) {\n if (isComponentOrHookName(node.id?.name)) depth++\n },\n 'FunctionDeclaration:exit'(node: any) {\n if (isComponentOrHookName(node.id?.name)) depth--\n },\n // const MyComp = () => {} / const useFoo = function () {}\n VariableDeclarator(node: any) {\n if (declaratorIsComponentOrHook(node)) depth++\n },\n 'VariableDeclarator:exit'(node: any) {\n if (declaratorIsComponentOrHook(node)) depth--\n },\n },\n }\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { createComponentContextTracker } from '../../utils/component-context'\n\nconst STORAGE_OBJECTS = new Set(['localStorage', 'sessionStorage'])\nconst STORAGE_METHODS = new Set(['getItem', 'setItem', 'removeItem'])\n\nexport const noRawLocalStorage: Rule = {\n meta: {\n id: 'pyreon/no-raw-localstorage',\n category: 'hooks',\n description:\n 'Suggest useStorage() instead of raw localStorage/sessionStorage inside a component or hook.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n // The rule's premise — \"use the reactive, cross-tab synced wrapper\" —\n // only applies inside a component / hook. Module-level config readers,\n // utility helpers, and storage-library internals legitimately use the\n // raw API. Foundation-package opt-out (e.g. `@pyreon/storage` itself)\n // belongs in the consuming project's lint config, not in rule source.\n const ctx = createComponentContextTracker()\n\n const callbacks: VisitorCallbacks = {\n ...ctx.callbacks,\n CallExpression(node: any) {\n if (!ctx.isInComponentOrHook()) return\n const callee = node.callee\n if (!callee || callee.type !== 'MemberExpression') return\n if (\n callee.object?.type === 'Identifier' &&\n STORAGE_OBJECTS.has(callee.object.name) &&\n callee.property?.type === 'Identifier' &&\n STORAGE_METHODS.has(callee.property.name)\n ) {\n context.report({\n message: `Raw \\`${callee.object.name}.${callee.property.name}()\\` — consider using \\`useStorage()\\` from \\`@pyreon/storage\\` for reactive, cross-tab synced storage.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { isPathExempt } from '../../utils/exempt-paths'\nimport { createComponentContextTracker } from '../../utils/component-context'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nconst TIMER_FNS = new Set(['setInterval', 'setTimeout'])\n\nexport const noRawSetInterval: Rule = {\n meta: {\n id: 'pyreon/no-raw-setinterval',\n category: 'hooks',\n description: 'Suggest wrapping setInterval/setTimeout in onMount for automatic cleanup.',\n severity: 'info',\n fixable: false,\n schema: { exemptPaths: 'string[]' },\n },\n create(context) {\n // Configurable `exemptPaths` — for packages that IMPLEMENT\n // `useInterval` / `useTimeout` (they can't use themselves).\n if (isPathExempt(context)) return {}\n\n // Only flag when *inside* a component / hook setup body. Module-level\n // timers, utility functions, and test callbacks have their own\n // lifecycle and don't need component-tied cleanup.\n const ctx = createComponentContextTracker()\n\n let mountDepth = 0\n const callbacks: VisitorCallbacks = {\n ...ctx.callbacks,\n CallExpression(node: any) {\n if (isCallTo(node, 'onMount')) {\n mountDepth++\n }\n\n if (mountDepth > 0) return\n if (!ctx.isInComponentOrHook()) return\n\n const callee = node.callee\n if (!callee || callee.type !== 'Identifier') return\n if (TIMER_FNS.has(callee.name)) {\n context.report({\n message: `\\`${callee.name}()\\` outside \\`onMount\\` — wrap in \\`onMount(() => { ... return () => clear... })\\` for automatic cleanup.`,\n span: getSpan(node),\n })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'onMount')) {\n mountDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isLogicalAndWithJSX } from '../../utils/ast'\n\nexport const noAndConditional: Rule = {\n meta: {\n id: 'pyreon/no-and-conditional',\n category: 'jsx',\n description: 'Prefer <Show> over `&&` with JSX in expression containers.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let jsxExpressionDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXExpressionContainer() {\n jsxExpressionDepth++\n },\n 'JSXExpressionContainer:exit'() {\n jsxExpressionDepth--\n },\n LogicalExpression(node: any) {\n if (jsxExpressionDepth === 0) return\n if (!isLogicalAndWithJSX(node)) return\n context.report({\n message: '`&&` with JSX — use `<Show>` for conditional rendering.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { extractImportInfo, type ImportInfo } from '../../utils/imports'\n\nexport const noChildrenAccess: Rule = {\n meta: {\n id: 'pyreon/no-children-access',\n category: 'jsx',\n description: 'Inform about direct props.children access in renderer files.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const imports: ImportInfo[] = []\n let isRendererFile = false\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (info) {\n imports.push(info)\n if (info.source === '@pyreon/runtime-server' || info.source === '@pyreon/runtime-dom') {\n isRendererFile = true\n }\n }\n },\n MemberExpression(node: any) {\n if (!isRendererFile) return\n if (\n node.object?.type === 'Identifier' &&\n node.property?.type === 'Identifier' &&\n node.property.name === 'children'\n ) {\n context.report({\n message:\n 'Direct `props.children` access in a renderer file — children are already merged via `mergeChildrenIntoProps`.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noClassName: Rule = {\n meta: {\n id: 'pyreon/no-classname',\n category: 'jsx',\n description: 'Use `class` instead of `className` — Pyreon uses standard HTML attributes.',\n severity: 'error',\n fixable: true,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXAttribute(node: any) {\n if (node.name?.type !== 'JSXIdentifier') return\n if (node.name.name !== 'className') return\n const nameSpan = getSpan(node.name)\n context.report({\n message: 'Use `class` instead of `className` — Pyreon uses standard HTML attributes.',\n span: getSpan(node),\n fix: { span: nameSpan, replacement: 'class' },\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noHtmlFor: Rule = {\n meta: {\n id: 'pyreon/no-htmlfor',\n category: 'jsx',\n description: 'Use `for` instead of `htmlFor` — Pyreon uses standard HTML attributes.',\n severity: 'error',\n fixable: true,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXAttribute(node: any) {\n if (node.name?.type !== 'JSXIdentifier') return\n if (node.name.name !== 'htmlFor') return\n const nameSpan = getSpan(node.name)\n context.report({\n message: 'Use `for` instead of `htmlFor` — Pyreon uses standard HTML attributes.',\n span: getSpan(node),\n fix: { span: nameSpan, replacement: 'for' },\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getJSXAttribute, getSpan } from '../../utils/ast'\n\nexport const noIndexAsBy: Rule = {\n meta: {\n id: 'pyreon/no-index-as-by',\n category: 'jsx',\n description: 'Disallow using index as `by` prop on <For> — use a unique key instead.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'For') return\n\n const byAttr = getJSXAttribute(node, 'by')\n if (!byAttr) return\n\n const value = byAttr.value\n if (!value || value.type !== 'JSXExpressionContainer') return\n\n const expr = value.expression\n if (!expr) return\n\n // Detect: by={(_, i) => i} or by={(item, index) => index}\n if (expr.type === 'ArrowFunctionExpression' || expr.type === 'FunctionExpression') {\n const params = expr.params\n if (!params || params.length < 2) return\n\n const secondParam = params[1]\n if (!secondParam || secondParam.type !== 'Identifier') return\n\n const indexName = secondParam.name\n const body = expr.body\n\n // Arrow expression body: (_, i) => i\n if (body?.type === 'Identifier' && body.name === indexName) {\n context.report({\n message:\n 'Using index as `by` prop on `<For>` — use a unique key from the data instead.',\n span: getSpan(byAttr),\n })\n }\n\n // Block body: (_, i) => { return i }\n if (body?.type === 'BlockStatement') {\n const stmts = body.body\n if (stmts?.length === 1) {\n const stmt = stmts[0]\n if (\n stmt.type === 'ReturnStatement' &&\n stmt.argument?.type === 'Identifier' &&\n stmt.argument.name === indexName\n ) {\n context.report({\n message:\n 'Using index as `by` prop on `<For>` — use a unique key from the data instead.',\n span: getSpan(byAttr),\n })\n }\n }\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isArrayMapCall } from '../../utils/ast'\n\nexport const noMapInJsx: Rule = {\n meta: {\n id: 'pyreon/no-map-in-jsx',\n category: 'jsx',\n description: 'Prefer <For> over .map() inside JSX for reactive list rendering.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let jsxDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXElement() {\n jsxDepth++\n },\n 'JSXElement:exit'() {\n jsxDepth--\n },\n JSXFragment() {\n jsxDepth++\n },\n 'JSXFragment:exit'() {\n jsxDepth--\n },\n CallExpression(node: any) {\n if (jsxDepth === 0) return\n if (!isArrayMapCall(node)) return\n // Check callback contains JSX\n const args = node.arguments\n if (!args || args.length === 0) return\n const callback = args[0]\n if (!callback) return\n context.report({\n message: '`.map()` in JSX — use `<For>` for reactive list rendering instead.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const noMissingForBy: Rule = {\n meta: {\n id: 'pyreon/no-missing-for-by',\n category: 'jsx',\n description: 'Warn when <For> is used without a `by` prop.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'For') return\n if (hasJSXAttribute(node, 'by')) return\n context.report({\n message:\n '`<For>` without `by` prop — provide a key function for efficient reconciliation.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nconst INPUT_TAGS = new Set(['input', 'textarea', 'select'])\n\nexport const noOnChange: Rule = {\n meta: {\n id: 'pyreon/no-onchange',\n category: 'jsx',\n description:\n 'Prefer `onInput` over `onChange` on input elements for keypress-by-keypress updates.',\n severity: 'warn',\n fixable: true,\n },\n create(context) {\n let currentTag: string | null = null\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (name?.type === 'JSXIdentifier' && INPUT_TAGS.has(name.name)) {\n currentTag = name.name\n } else {\n currentTag = null\n }\n\n if (!currentTag) return\n const attrs = node.attributes ?? []\n for (const attr of attrs) {\n if (\n attr.type === 'JSXAttribute' &&\n attr.name?.type === 'JSXIdentifier' &&\n attr.name.name === 'onChange'\n ) {\n const nameSpan = getSpan(attr.name)\n context.report({\n message: `Use \\`onInput\\` instead of \\`onChange\\` on \\`<${currentTag}>\\` for keypress-by-keypress updates.`,\n span: getSpan(attr),\n fix: { span: nameSpan, replacement: 'onInput' },\n })\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isDestructuring } from '../../utils/ast'\n\nfunction containsJSXReturn(node: any): boolean {\n if (!node) return false\n if (node.type === 'JSXElement' || node.type === 'JSXFragment') return true\n if (node.type === 'ParenthesizedExpression') return containsJSXReturn(node.expression)\n\n if (node.type === 'BlockStatement') {\n for (const stmt of node.body ?? []) {\n if (stmt.type === 'ReturnStatement' && containsJSXReturn(stmt.argument)) {\n return true\n }\n }\n }\n return false\n}\n\n/**\n * Extract destructured property names from an ObjectPattern.\n * Returns names for the fix suggestion.\n */\nfunction getDestructuredNames(pattern: any): string[] {\n if (pattern.type !== 'ObjectPattern') return []\n const names: string[] = []\n for (const prop of pattern.properties ?? []) {\n if (prop.type === 'ObjectProperty' && prop.key?.type === 'Identifier') {\n names.push(prop.key.name)\n }\n }\n return names\n}\n\nexport const noPropsDestructure: Rule = {\n meta: {\n id: 'pyreon/no-props-destructure',\n category: 'jsx',\n description:\n 'Disallow destructuring props in component functions — breaks reactive prop tracking. Use props.x or splitProps().',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n let functionDepth = 0\n // oxc visitor doesn't pass `parent` to callbacks — previous\n // `parent?.type === 'CallExpression'` check was silently inert. Pre-mark\n // function nodes that appear as CallExpression arguments on the way in.\n const callArgFns = new WeakSet<any>()\n\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n for (const arg of node.arguments ?? []) {\n if (\n arg?.type === 'ArrowFunctionExpression' ||\n arg?.type === 'FunctionExpression' ||\n arg?.type === 'FunctionDeclaration'\n ) {\n callArgFns.add(arg)\n }\n }\n },\n ArrowFunctionExpression(node: any) {\n functionDepth++\n checkFunction(node, context, functionDepth, callArgFns)\n },\n 'ArrowFunctionExpression:exit'() {\n functionDepth--\n },\n FunctionDeclaration(node: any) {\n functionDepth++\n checkFunction(node, context, functionDepth, callArgFns)\n },\n 'FunctionDeclaration:exit'() {\n functionDepth--\n },\n FunctionExpression(node: any) {\n functionDepth++\n checkFunction(node, context, functionDepth, callArgFns)\n },\n 'FunctionExpression:exit'() {\n functionDepth--\n },\n }\n return callbacks\n },\n}\n\nfunction checkFunction(node: any, context: any, depth: number, callArgFns: WeakSet<any>) {\n const params = node.params\n if (!params || params.length === 0) return\n\n const firstParam = params[0]\n if (!isDestructuring(firstParam)) return\n\n // Skip HOC inner functions (depth > 1)\n if (depth > 1) return\n\n // Skip functions passed as arguments to HOC factories\n // e.g. createLink(({ href, ...rest }) => <a {...rest} />)\n if (callArgFns.has(node)) return\n\n const body = node.body\n if (!body) return\n\n if (containsJSXReturn(body)) {\n const names = getDestructuredNames(firstParam)\n const hasRest = (firstParam.properties ?? []).some((p: any) => p.type === 'RestElement')\n\n let suggestion = 'Use `props.x` pattern for reactive prop access.'\n if (names.length > 0) {\n const propsAccess = names.map((n) => `props.${n}`).join(', ')\n suggestion = `Use \\`props\\` parameter and access as ${propsAccess}.`\n if (hasRest) {\n suggestion += ` For rest props, use \\`splitProps(props, [${names.map((n) => `'${n}'`).join(', ')}])\\`.`\n }\n }\n\n context.report({\n message:\n `Destructured props in component function — breaks reactive prop tracking. ${suggestion}`,\n span: getSpan(firstParam),\n })\n }\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isTernaryWithJSX } from '../../utils/ast'\n\nexport const noTernaryConditional: Rule = {\n meta: {\n id: 'pyreon/no-ternary-conditional',\n category: 'jsx',\n description: 'Prefer <Show> over ternary expressions with JSX branches.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let jsxExpressionDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXExpressionContainer() {\n jsxExpressionDepth++\n },\n 'JSXExpressionContainer:exit'() {\n jsxExpressionDepth--\n },\n ConditionalExpression(node: any) {\n if (jsxExpressionDepth === 0) return\n if (!isTernaryWithJSX(node)) return\n context.report({\n message: 'Ternary with JSX — use `<Show>` for more efficient conditional rendering.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getJSXAttribute, getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const useByNotKey: Rule = {\n meta: {\n id: 'pyreon/use-by-not-key',\n category: 'jsx',\n description:\n 'Use `by` prop on <For> instead of `key` — JSX reserves `key` for VNode reconciliation.',\n severity: 'error',\n fixable: true,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const tagName = node.name?.type === 'JSXIdentifier' ? node.name.name : null\n if (tagName !== 'For') return\n const keyAttr = getJSXAttribute(node, 'key')\n if (!keyAttr) return\n if (hasJSXAttribute(node, 'by')) return // already has by\n\n const attrSpan = getSpan(keyAttr.name)\n context.report({\n message:\n 'Use `by` prop on `<For>` instead of `key` — JSX reserves `key` for VNode reconciliation.',\n span: getSpan(keyAttr),\n fix: { span: attrSpan, replacement: 'by' },\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nconst DOM_METHODS = new Set([\n 'querySelector',\n 'querySelectorAll',\n 'getElementById',\n 'getElementsByClassName',\n 'getElementsByTagName',\n])\n\nexport const noDomInSetup: Rule = {\n meta: {\n id: 'pyreon/no-dom-in-setup',\n category: 'lifecycle',\n description: 'Warn when DOM query methods are used outside onMount or effect.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let safeDepth = 0\n function isSafeContextCall(node: any): boolean {\n // Lifecycle + effect hooks only run post-mount in a browser.\n // `onUnmount` / `onCleanup` fire after the component has mounted so\n // the DOM exists. `renderEffect` is the signal-system equivalent of\n // `effect`. `requestAnimationFrame` only schedules its callback\n // inside a browser frame, so its body is always post-setup execution.\n return (\n isCallTo(node, 'onMount') ||\n isCallTo(node, 'onUnmount') ||\n isCallTo(node, 'onCleanup') ||\n isCallTo(node, 'effect') ||\n isCallTo(node, 'renderEffect') ||\n isCallTo(node, 'requestAnimationFrame')\n )\n }\n // `if (typeof document === 'undefined') return|throw` at the head of a\n // function makes the rest of the body implicitly browser-safe — the\n // SSR path bailed out. Same heuristic as `no-window-in-ssr`.\n function isNegatedTypeofDocument(test: any): boolean {\n if (!test) return false\n if (\n test.type === 'BinaryExpression' &&\n (test.operator === '===' || test.operator === '==') &&\n test.left?.type === 'UnaryExpression' &&\n test.left.operator === 'typeof' &&\n test.left.argument?.type === 'Identifier' &&\n (test.left.argument.name === 'document' || test.left.argument.name === 'window')\n )\n return true\n return false\n }\n function isEarlyReturnDocumentGuard(stmt: any): boolean {\n if (!stmt || stmt.type !== 'IfStatement') return false\n if (!isNegatedTypeofDocument(stmt.test)) return false\n const c = stmt.consequent\n const isTerminator = (s: any): boolean =>\n s?.type === 'ReturnStatement' || s?.type === 'ThrowStatement'\n if (isTerminator(c)) return true\n if (c?.type === 'BlockStatement' && c.body.length === 1 && isTerminator(c.body[0]))\n return true\n return false\n }\n // Per-function depth bumps from early-return guards — popped on exit.\n const earlyReturnStack: number[] = []\n function pushFunctionScope(node: any) {\n const body = node?.body\n const stmts = body?.type === 'BlockStatement' ? body.body : null\n let bumps = 0\n if (stmts && stmts.length > 0 && isEarlyReturnDocumentGuard(stmts[0])) {\n bumps = 1\n safeDepth++\n }\n earlyReturnStack.push(bumps)\n }\n function popFunctionScope() {\n const bumps = earlyReturnStack.pop() ?? 0\n if (bumps > 0) safeDepth -= bumps\n }\n const callbacks: VisitorCallbacks = {\n FunctionDeclaration: pushFunctionScope,\n 'FunctionDeclaration:exit': popFunctionScope,\n FunctionExpression: pushFunctionScope,\n 'FunctionExpression:exit': popFunctionScope,\n ArrowFunctionExpression: pushFunctionScope,\n 'ArrowFunctionExpression:exit': popFunctionScope,\n CallExpression(node: any) {\n if (isSafeContextCall(node)) safeDepth++\n\n if (safeDepth > 0) return\n\n // Check for document.querySelector() etc.\n const callee = node.callee\n if (\n callee?.type === 'MemberExpression' &&\n callee.object?.type === 'Identifier' &&\n callee.object.name === 'document' &&\n callee.property?.type === 'Identifier' &&\n DOM_METHODS.has(callee.property.name)\n ) {\n context.report({\n message: `\\`document.${callee.property.name}()\\` outside \\`onMount\\`/\\`effect\\` — DOM is not available during SSR or setup phase.`,\n span: getSpan(node),\n })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isSafeContextCall(node)) safeDepth--\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noEffectInMount: Rule = {\n meta: {\n id: 'pyreon/no-effect-in-mount',\n category: 'lifecycle',\n description:\n 'Inform when effect() is created inside onMount — effects are typically created at setup time.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n let mountDepth = 0\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (isCallTo(node, 'onMount')) {\n mountDepth++\n }\n if (mountDepth > 0 && isCallTo(node, 'effect')) {\n context.report({\n message:\n '`effect()` inside `onMount` — effects are typically created at component setup time, not inside lifecycle hooks.',\n span: getSpan(node),\n })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'onMount')) {\n mountDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nconst NEEDS_CLEANUP = new Set(['setInterval', 'addEventListener'])\n\nexport const noMissingCleanup: Rule = {\n meta: {\n id: 'pyreon/no-missing-cleanup',\n category: 'lifecycle',\n description:\n 'Warn when onMount uses setInterval/addEventListener without returning a cleanup function.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'onMount')) return\n const args = node.arguments\n if (!args || args.length === 0) return\n\n const fn = args[0]\n if (!fn) return\n if (fn.type !== 'ArrowFunctionExpression' && fn.type !== 'FunctionExpression') return\n\n const body = fn.body\n if (!body) return\n\n // Only check block bodies\n if (body.type !== 'BlockStatement') return\n\n let hasCleanupTarget = false\n let hasReturn = false\n\n function walk(n: any) {\n if (!n) return\n if (n.type === 'CallExpression') {\n const callee = n.callee\n if (callee?.type === 'Identifier' && NEEDS_CLEANUP.has(callee.name)) {\n hasCleanupTarget = true\n }\n if (\n callee?.type === 'MemberExpression' &&\n callee.property?.type === 'Identifier' &&\n NEEDS_CLEANUP.has(callee.property.name)\n ) {\n hasCleanupTarget = true\n }\n }\n if (n.type === 'ReturnStatement' && n.argument) {\n hasReturn = true\n }\n for (const key of Object.keys(n)) {\n const child = n[key]\n if (child && typeof child === 'object') {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item.type === 'string') walk(item)\n }\n } else if (typeof child.type === 'string') {\n walk(child)\n }\n }\n }\n }\n\n walk(body)\n\n if (hasCleanupTarget && !hasReturn) {\n context.report({\n message:\n '`onMount` uses `setInterval`/`addEventListener` without returning a cleanup function — this will cause a memory leak.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noMountInEffect: Rule = {\n meta: {\n id: 'pyreon/no-mount-in-effect',\n category: 'lifecycle',\n description: 'Warn when onMount is called inside effect().',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let effectDepth = 0\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (isCallTo(node, 'effect')) {\n effectDepth++\n }\n if (effectDepth > 0 && isCallTo(node, 'onMount')) {\n context.report({\n message:\n '`onMount` inside `effect()` — `onMount` runs once on mount, not on every effect re-run.',\n span: getSpan(node),\n })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'effect')) {\n effectDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { HEAVY_PACKAGES } from '../../utils/imports'\n\nexport const noEagerImport: Rule = {\n meta: {\n id: 'pyreon/no-eager-import',\n category: 'performance',\n description: 'Suggest lazy-loading heavy Pyreon packages (charts, code, document, flow).',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const source = node.source?.value as string\n if (!source) return\n if (HEAVY_PACKAGES.has(source)) {\n context.report({\n message: `Static import of \\`${source}\\` — consider using \\`lazy()\\` or dynamic \\`import()\\` to reduce initial bundle size.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noEffectInFor: Rule = {\n meta: {\n id: 'pyreon/no-effect-in-for',\n category: 'performance',\n description:\n 'Warn when effect() is created inside <For> — creates effects per item on every reconciliation.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let forJsxDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (name?.type === 'JSXIdentifier' && name.name === 'For') {\n forJsxDepth++\n }\n },\n JSXClosingElement(node: any) {\n const name = node.name\n if (name?.type === 'JSXIdentifier' && name.name === 'For') {\n forJsxDepth--\n }\n },\n CallExpression(node: any) {\n if (forJsxDepth === 0) return\n if (isCallTo(node, 'effect')) {\n context.report({\n message:\n '`effect()` inside `<For>` — this creates a new effect for every item on each reconciliation. Lift the effect outside.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, hasJSXAttribute } from '../../utils/ast'\n\nexport const noLargeForWithoutBy: Rule = {\n meta: {\n id: 'pyreon/no-large-for-without-by',\n category: 'performance',\n description:\n 'Error when <For> is used without a `by` prop — critical for reconciliation performance.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXOpeningElement(node: any) {\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'For') return\n if (hasJSXAttribute(node, 'by')) return\n context.report({\n message:\n '`<For>` without `by` prop — provide a key function for efficient reconciliation.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const preferShowOverDisplay: Rule = {\n meta: {\n id: 'pyreon/prefer-show-over-display',\n category: 'performance',\n description: 'Suggest <Show> over conditional `display` style property in JSX.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXAttribute(node: any) {\n if (node.name?.type !== 'JSXIdentifier' || node.name.name !== 'style') return\n const value = node.value\n if (!value || value.type !== 'JSXExpressionContainer') return\n const expr = value.expression\n if (!expr || expr.type !== 'ObjectExpression') return\n\n for (const prop of expr.properties ?? []) {\n if (prop.type !== 'Property') continue\n const key = prop.key\n if (!key) continue\n const propName =\n key.type === 'Identifier' ? key.name : key.type === 'Literal' ? key.value : null\n if (propName === 'display') {\n // Check if the value is conditional\n const val = prop.value\n if (\n val?.type === 'ConditionalExpression' ||\n val?.type === 'LogicalExpression' ||\n val?.type === 'CallExpression'\n ) {\n context.report({\n message:\n 'Conditional `display` style — consider using `<Show>` for conditional rendering instead of toggling CSS display.',\n span: getSpan(prop),\n })\n }\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\n// `use…`/`get…`/`is…`/`has…` are conventional hook/getter prefixes — not\n// signal reads. `[A-Z]…` covers component invocations. The skip-names set\n// covers framework VNode-producing helpers whose call sites always produce\n// JSX, not signal values:\n// - `render` — `@pyreon/ui-core` render-prop helper\n// - `h` — `@pyreon/core` hyperscript (JSX runtime)\n// - `cloneVNode` — `@pyreon/core` VNode-tree cloner (used by kinetic)\n// Matching is on full identifier name, so user-defined signals with these\n// exact names would slip through; rename to `rendered`/`hyperscript`/etc.\n// or move the read outside JSX.\nconst SKIP_NAMES = new Set(['render', 'h', 'cloneVNode'])\nconst SKIP_PREFIXES = /^(use|get|is|has|[A-Z])/\n\nexport const noBareSignalInJsx: Rule = {\n meta: {\n id: 'pyreon/no-bare-signal-in-jsx',\n category: 'reactivity',\n description:\n 'Disallow bare signal calls in JSX text positions. Wrap in `() =>` for reactivity.',\n severity: 'error',\n fixable: true,\n },\n create(context) {\n let jsxDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXElement() {\n jsxDepth++\n },\n 'JSXElement:exit'() {\n jsxDepth--\n },\n JSXFragment() {\n jsxDepth++\n },\n 'JSXFragment:exit'() {\n jsxDepth--\n },\n JSXExpressionContainer(node: any) {\n if (jsxDepth === 0) return\n const expr = node.expression\n if (!expr || expr.type !== 'CallExpression') return\n const callee = expr.callee\n if (!callee || callee.type !== 'Identifier') return\n\n const name: string = callee.name\n if (SKIP_NAMES.has(name) || SKIP_PREFIXES.test(name)) return\n\n const span = getSpan(node)\n const source = context.getSourceText()\n const original = source.slice(span.start, span.end)\n // {count()} → {() => count()}\n const inner = original.slice(1, -1) // strip { }\n const fixed = `{() => ${inner}}`\n\n context.report({\n message: `Bare signal call \\`${name}()\\` in JSX text — wrap in \\`() => ${name}()\\` for fine-grained reactivity.`,\n span,\n fix: { span, replacement: fixed },\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\n/**\n * Detects destructuring the return value of useContext().\n *\n * `const { mode } = useContext(ctx)` loses reactivity when the context\n * provides getter properties. The value is captured once at setup time.\n *\n * Correct: `const ctx = useContext(Ctx)` then read `ctx.mode` lazily.\n */\nexport const noContextDestructure: Rule = {\n meta: {\n id: 'pyreon/no-context-destructure',\n category: 'reactivity',\n description:\n 'Disallow destructuring useContext() — it breaks reactivity when context provides getters.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n VariableDeclarator(node: any) {\n // Match: const { x } = useContext(...)\n const id = node.id\n const init = node.init\n if (!id || !init) return\n if (id.type !== 'ObjectPattern') return\n if (\n init.type !== 'CallExpression' ||\n init.callee?.type !== 'Identifier' ||\n init.callee.name !== 'useContext'\n )\n return\n\n context.report({\n message:\n 'Destructuring useContext() captures values once — reactive getters lose reactivity. Keep the object reference: `const ctx = useContext(Ctx)` and access `ctx.mode` lazily.',\n span: getSpan(id),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nfunction isUpdateCall(node: any): boolean {\n return (\n node.type === 'CallExpression' &&\n node.callee?.type === 'MemberExpression' &&\n node.callee.property?.type === 'Identifier' &&\n node.callee.property.name === 'update'\n )\n}\n\nexport const noEffectAssignment: Rule = {\n meta: {\n id: 'pyreon/no-effect-assignment',\n category: 'reactivity',\n description: 'Warn when an effect only contains a single .update() call.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'effect')) return\n const args = node.arguments\n if (!args || args.length === 0) return\n\n const fn = args[0]\n if (!fn) return\n\n let body: any = null\n if (fn.type === 'ArrowFunctionExpression' || fn.type === 'FunctionExpression') {\n body = fn.body\n }\n if (!body) return\n\n // Arrow with expression body\n if (isUpdateCall(body)) {\n context.report({\n message:\n 'Effect contains a single `.update()` — consider using `computed()` for derived values.',\n span: getSpan(node),\n })\n return\n }\n\n // Block body with single statement\n if (body.type === 'BlockStatement') {\n const stmts = body.body\n if (stmts && stmts.length === 1) {\n const stmt = stmts[0]\n if (stmt.type === 'ExpressionStatement' && isUpdateCall(stmt.expression)) {\n context.report({\n message:\n 'Effect contains a single `.update()` — consider using `computed()` for derived values.',\n span: getSpan(node),\n })\n }\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noNestedEffect: Rule = {\n meta: {\n id: 'pyreon/no-nested-effect',\n category: 'reactivity',\n description: 'Warn against nesting effect() inside another effect().',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let effectDepth = 0\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'effect')) return\n if (effectDepth > 0) {\n context.report({\n message: 'Nested `effect()` — consider using `computed()` for derived values instead.',\n span: getSpan(node),\n })\n }\n effectDepth++\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'effect')) {\n effectDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo, isPeekCall } from '../../utils/ast'\n\nexport const noPeekInTracked: Rule = {\n meta: {\n id: 'pyreon/no-peek-in-tracked',\n category: 'reactivity',\n description: 'Disallow .peek() inside effect() or computed() — it bypasses tracking.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n let trackedDepth = 0\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (isCallTo(node, 'effect') || isCallTo(node, 'computed')) {\n trackedDepth++\n }\n if (trackedDepth > 0 && isPeekCall(node)) {\n context.report({\n message:\n '`.peek()` inside a tracked scope (effect/computed) bypasses dependency tracking — use a normal signal read instead.',\n span: getSpan(node),\n })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'effect') || isCallTo(node, 'computed')) {\n trackedDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noSignalInLoop: Rule = {\n meta: {\n id: 'pyreon/no-signal-in-loop',\n category: 'reactivity',\n description: 'Disallow creating signals or computeds inside loops.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n let loopDepth = 0\n const callbacks: VisitorCallbacks = {\n ForStatement() {\n loopDepth++\n },\n 'ForStatement:exit'() {\n loopDepth--\n },\n ForInStatement() {\n loopDepth++\n },\n 'ForInStatement:exit'() {\n loopDepth--\n },\n ForOfStatement() {\n loopDepth++\n },\n 'ForOfStatement:exit'() {\n loopDepth--\n },\n WhileStatement() {\n loopDepth++\n },\n 'WhileStatement:exit'() {\n loopDepth--\n },\n DoWhileStatement() {\n loopDepth++\n },\n 'DoWhileStatement:exit'() {\n loopDepth--\n },\n CallExpression(node: any) {\n if (loopDepth === 0) return\n const callee = node.callee\n if (!callee || callee.type !== 'Identifier') return\n if (callee.name === 'signal' || callee.name === 'computed') {\n context.report({\n message: `\\`${callee.name}()\\` inside a loop — signals should be created once at component setup, not on every iteration.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nfunction isComponentTag(name: string): boolean {\n return name.length > 0 && name[0] === name[0]?.toUpperCase() && name[0] !== name[0]?.toLowerCase()\n}\n\n/**\n * Warn when a known signal/computed is called in a component prop position.\n * Component props are evaluated once at mount — signal reads are NOT reactive\n * unless the compiler wraps them with _rp(). The compiler handles this\n * automatically, but this rule catches manual h() calls and educates developers.\n */\nexport const noSignalInProps: Rule = {\n meta: {\n id: 'pyreon/no-signal-in-props',\n category: 'reactivity',\n description:\n 'Signal call in component prop — value captured once unless compiler wraps it. Use props.x pattern for reactivity.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXExpressionContainer(node: any) {\n const expr = node.expression\n if (!expr || expr.type !== 'CallExpression') return\n const callee = expr.callee\n if (!callee || callee.type !== 'Identifier') return\n\n const source = context.getSourceText()\n const start = node.start as number\n\n let i = start - 1\n while (i >= 0 && source[i] !== '<' && source[i] !== '>') i--\n if (i < 0 || source[i] !== '<') return\n\n const tagStart = i + 1\n let tagEnd = tagStart\n while (tagEnd < source.length && /[\\w.]/.test(source[tagEnd] ?? '')) tagEnd++\n const tagName = source.slice(tagStart, tagEnd)\n\n if (!tagName || !isComponentTag(tagName)) return\n\n context.report({\n message: `Signal call in <${tagName}> prop — use props.x pattern inside the component for reactive access.`,\n span: getSpan(expr),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const noSignalLeak: Rule = {\n meta: {\n id: 'pyreon/no-signal-leak',\n category: 'reactivity',\n description: 'Warn about unused signal declarations (potential leaks).',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const signalDecls = new Map<\n string,\n { span: { start: number; end: number }; declStart: number; declEnd: number }\n >()\n const identifierOccurrences = new Map<string, Array<{ start: number; end: number }>>()\n\n const callbacks: VisitorCallbacks = {\n VariableDeclarator(node: any) {\n const init = node.init\n if (!init || !isCallTo(init, 'signal')) return\n const id = node.id\n if (!id || id.type !== 'Identifier') return\n signalDecls.set(id.name, {\n span: getSpan(node),\n declStart: id.start as number,\n declEnd: id.end as number,\n })\n },\n Identifier(node: any) {\n const name: string = node.name\n const existing = identifierOccurrences.get(name)\n if (existing) {\n existing.push({ start: node.start as number, end: node.end as number })\n } else {\n identifierOccurrences.set(name, [\n { start: node.start as number, end: node.end as number },\n ])\n }\n },\n 'Program:exit'() {\n for (const [name, { span, declStart, declEnd }] of signalDecls) {\n const occurrences = identifierOccurrences.get(name) ?? []\n // Filter out the declaration identifier itself\n const usages = occurrences.filter((o) => o.start !== declStart || o.end !== declEnd)\n if (usages.length === 0) {\n context.report({\n message: `Signal \\`${name}\\` is declared but never used — this may be a signal leak.`,\n span,\n })\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo, isSetCall } from '../../utils/ast'\nimport { isPathExempt } from '../../utils/exempt-paths'\n\ninterface ScopeInfo {\n setCalls: Array<{ span: { start: number; end: number } }>\n hasBatch: boolean\n insideBatch: boolean\n node: any\n}\n\nexport const noUnbatchedUpdates: Rule = {\n meta: {\n id: 'pyreon/no-unbatched-updates',\n category: 'reactivity',\n description: 'Warn when 3+ .set() calls occur in the same function without batch().',\n severity: 'warn',\n fixable: false,\n schema: { exemptPaths: 'string[]' },\n },\n create(context) {\n if (isPathExempt(context)) return {}\n const scopeStack: ScopeInfo[] = []\n let batchDepth = 0\n\n function enterScope(node: any) {\n scopeStack.push({ setCalls: [], hasBatch: false, insideBatch: batchDepth > 0, node })\n }\n\n function exitScope() {\n const scope = scopeStack.pop()\n if (!scope) return\n if (!scope.hasBatch && !scope.insideBatch && scope.setCalls.length >= 3) {\n context.report({\n message: `${scope.setCalls.length} signal \\`.set()\\` calls without \\`batch()\\` — wrap in \\`batch(() => { ... })\\` to avoid unnecessary re-renders.`,\n span: getSpan(scope.node),\n })\n }\n }\n\n const callbacks: VisitorCallbacks = {\n FunctionDeclaration(node: any) {\n enterScope(node)\n },\n 'FunctionDeclaration:exit'() {\n exitScope()\n },\n FunctionExpression(node: any) {\n enterScope(node)\n },\n 'FunctionExpression:exit'() {\n exitScope()\n },\n ArrowFunctionExpression(node: any) {\n enterScope(node)\n },\n 'ArrowFunctionExpression:exit'() {\n exitScope()\n },\n CallExpression(node: any) {\n const currentScope = scopeStack.length > 0 ? scopeStack[scopeStack.length - 1] : undefined\n if (isCallTo(node, 'batch')) {\n batchDepth++\n if (currentScope) {\n currentScope.hasBatch = true\n }\n }\n if (currentScope && isSetCall(node)) {\n currentScope.setCalls.push({ span: getSpan(node) })\n }\n },\n 'CallExpression:exit'(node: any) {\n if (isCallTo(node, 'batch')) {\n batchDepth--\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo, isSetCall } from '../../utils/ast'\n\nexport const preferComputed: Rule = {\n meta: {\n id: 'pyreon/prefer-computed',\n category: 'reactivity',\n description: 'Suggest computed() when an effect only contains a single .set() call.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'effect')) return\n const args = node.arguments\n if (!args || args.length === 0) return\n\n const fn = args[0]\n if (!fn) return\n\n let body: any = null\n if (fn.type === 'ArrowFunctionExpression' || fn.type === 'FunctionExpression') {\n body = fn.body\n }\n if (!body) return\n\n // Arrow with expression body: effect(() => x.set(y))\n if (body.type === 'CallExpression' && isSetCall(body)) {\n context.report({\n message:\n 'Effect contains a single `.set()` — consider using `computed()` instead for derived values.',\n span: getSpan(node),\n })\n return\n }\n\n // Block body with single statement: effect(() => { x.set(y) })\n if (body.type === 'BlockStatement') {\n const stmts = body.body\n if (stmts && stmts.length === 1) {\n const stmt = stmts[0]\n if (stmt.type === 'ExpressionStatement' && isSetCall(stmt.expression)) {\n context.report({\n message:\n 'Effect contains a single `.set()` — consider using `computed()` instead for derived values.',\n span: getSpan(node),\n })\n }\n }\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getJSXAttribute, getSpan } from '../../utils/ast'\nimport { extractImportInfo } from '../../utils/imports'\n\nconst EXTERNAL_PREFIXES = ['http://', 'https://', 'mailto:', 'tel:']\n\nexport const noHrefNavigation: Rule = {\n meta: {\n id: 'pyreon/no-href-navigation',\n category: 'router',\n description:\n 'Warn when `<a href>` is used in files that import @pyreon/router — use `<Link>` instead.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let importsRouter = false\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (info && info.source === '@pyreon/router') {\n importsRouter = true\n }\n },\n JSXOpeningElement(node: any) {\n if (!importsRouter) return\n const name = node.name\n if (!name || name.type !== 'JSXIdentifier' || name.name !== 'a') return\n\n const hrefAttr = getJSXAttribute(node, 'href')\n if (!hrefAttr) return\n\n // Get the href value\n const value = hrefAttr.value\n if (value?.type === 'Literal' && typeof value.value === 'string') {\n const href: string = value.value\n // Skip external URLs and anchor links\n if (href.startsWith('#') || EXTERNAL_PREFIXES.some((p) => href.startsWith(p))) return\n }\n\n context.report({\n message:\n '`<a href>` in a router file — use `<Link>` or `<RouterLink>` for client-side navigation.',\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo, isMemberCallTo } from '../../utils/ast'\n\nexport const noImperativeNavigateInRender: Rule = {\n meta: {\n id: 'pyreon/no-imperative-navigate-in-render',\n category: 'router',\n description:\n 'Error when navigate() or router.push() is called at the top level of a component — causes infinite render loops.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n // The rule fires when `navigate()` / `router.push()` runs synchronously\n // as part of rendering the component — the infinite-render-loop case.\n //\n // The render path consists of the component body itself PLUS any nested\n // function that is called synchronously from the render body (IIFEs,\n // locally-bound fns that are invoked immediately). Nested functions\n // that are stored (assigned to a const, returned, passed as a callback\n // to an event handler / setTimeout / .then) are deferred execution and\n // don't contribute to the render-loop bug.\n let componentBodyDepth = 0\n // Depth inside a nested non-component function AT ALL — used to scope\n // call-tracking (we only record nested-fn calls while inside a\n // component body, not in module-level code).\n let nestedFnDepth = 0\n // Arrow/Function expressions that are the direct init of a PascalCase\n // `VariableDeclarator` (= component assignment) — marked so the\n // ArrowFn/FunctionExpression visitor knows to NOT count them as nested.\n const componentInits = new WeakSet<any>()\n // Names of locally-bound nested fns (e.g. `const fn = () => router.push(...)`)\n // whose body contains a dangerous navigation call. Populated during the\n // nested fn's walk; checked on synchronous `fn()` calls in the render\n // body. Stack-scoped to the enclosing component so nested components\n // don't leak bindings.\n const dangerousBindings: Array<Set<string>> = []\n // When we're inside a nested fn whose binding might be dangerous, mark\n // the current nested fn as \"contains navigate\" if we see one.\n const nestedFnStack: Array<{ containsNavigate: boolean; bindingName: string | null }> = []\n\n function isComponentFunctionDecl(node: any): boolean {\n return /^[A-Z]/.test(node.id?.name ?? '')\n }\n\n function enterNestedFn(node: any, bindingName: string | null) {\n nestedFnDepth++\n nestedFnStack.push({ containsNavigate: false, bindingName })\n }\n function exitNestedFn() {\n nestedFnDepth--\n const frame = nestedFnStack.pop()\n if (!frame) return\n if (frame.containsNavigate && frame.bindingName && dangerousBindings.length > 0) {\n dangerousBindings[dangerousBindings.length - 1]!.add(frame.bindingName)\n }\n }\n\n function isNavigateCall(node: any): boolean {\n return isCallTo(node, 'navigate') || isMemberCallTo(node, 'router', 'push')\n }\n\n const callbacks: VisitorCallbacks = {\n FunctionDeclaration(node: any) {\n if (isComponentFunctionDecl(node)) {\n componentBodyDepth++\n dangerousBindings.push(new Set())\n } else if (componentBodyDepth > 0) {\n enterNestedFn(node, node.id?.type === 'Identifier' ? node.id.name : null)\n }\n },\n 'FunctionDeclaration:exit'(node: any) {\n if (isComponentFunctionDecl(node)) {\n componentBodyDepth--\n dangerousBindings.pop()\n } else if (componentBodyDepth > 0) {\n exitNestedFn()\n }\n },\n VariableDeclarator(node: any) {\n if (\n /^[A-Z]/.test(node.id?.name ?? '') &&\n (node.init?.type === 'ArrowFunctionExpression' || node.init?.type === 'FunctionExpression')\n ) {\n componentBodyDepth++\n dangerousBindings.push(new Set())\n componentInits.add(node.init)\n }\n },\n 'VariableDeclarator:exit'(node: any) {\n if (\n /^[A-Z]/.test(node.id?.name ?? '') &&\n (node.init?.type === 'ArrowFunctionExpression' || node.init?.type === 'FunctionExpression')\n ) {\n componentBodyDepth--\n dangerousBindings.pop()\n }\n },\n ArrowFunctionExpression(node: any) {\n if (componentInits.has(node)) return\n if (componentBodyDepth > 0) {\n // Binding name comes from the parent VariableDeclarator if the\n // arrow is its init — e.g. `const fn = () => …`. Parent is not\n // passed by oxc, so we rely on order: `VariableDeclarator` visits\n // before its init. We patch the binding name in a pre-visitor pass.\n enterNestedFn(node, bindingAssignmentNames.get(node) ?? null)\n }\n },\n 'ArrowFunctionExpression:exit'(node: any) {\n if (componentInits.has(node)) return\n if (componentBodyDepth > 0) exitNestedFn()\n },\n FunctionExpression(node: any) {\n if (componentInits.has(node)) return\n if (componentBodyDepth > 0) {\n enterNestedFn(node, bindingAssignmentNames.get(node) ?? null)\n }\n },\n 'FunctionExpression:exit'(node: any) {\n if (componentInits.has(node)) return\n if (componentBodyDepth > 0) exitNestedFn()\n },\n CallExpression(node: any) {\n if (componentBodyDepth <= 0) return\n\n // Direct `navigate()` / `router.push()` call. At render-body depth\n // (nestedFnDepth === 0) it's the classic infinite-loop bug. Inside\n // a nested fn, mark the enclosing frame as dangerous (so a later\n // sync call of that fn's binding re-surfaces the issue).\n if (isNavigateCall(node)) {\n if (nestedFnDepth === 0) {\n context.report({\n message:\n 'Imperative navigation at the top level of a component — this runs on every render and causes infinite loops. Move inside `onMount`, `effect`, or an event handler.',\n span: getSpan(node),\n })\n } else if (nestedFnStack.length > 0) {\n nestedFnStack[nestedFnStack.length - 1]!.containsNavigate = true\n }\n return\n }\n\n // Sync invocation (at render-body depth) of a locally-bound fn\n // whose body contains `navigate()`. `const fn = () => router.push();\n // fn()` IS the infinite-loop bug — the previous rewrite missed it.\n if (\n nestedFnDepth === 0 &&\n node.callee?.type === 'Identifier' &&\n dangerousBindings.length > 0 &&\n dangerousBindings[dangerousBindings.length - 1]!.has(node.callee.name)\n ) {\n context.report({\n message:\n 'Synchronous call of a nested function that performs imperative navigation — this runs during render and causes infinite loops. Move the call inside `onMount`, `effect`, or an event handler.',\n span: getSpan(node),\n })\n }\n },\n }\n\n // Pre-walk: for each `VariableDeclarator` whose init is an ArrowFn or\n // FunctionExpression, stash the binding name against the init node so\n // the ArrowFn/FunctionExpression visitor can retrieve it without needing\n // parent-in-visitor (oxc doesn't pass parent).\n const bindingAssignmentNames = new WeakMap<any, string>()\n callbacks.VariableDeclaration = (node: any) => {\n for (const decl of node.declarations ?? []) {\n if (\n decl.id?.type === 'Identifier' &&\n (decl.init?.type === 'ArrowFunctionExpression' ||\n decl.init?.type === 'FunctionExpression')\n ) {\n bindingAssignmentNames.set(decl.init, decl.id.name)\n }\n }\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { extractImportInfo } from '../../utils/imports'\n\nfunction isCatchAllPath(value: string): boolean {\n return value === '*' || value.endsWith('*')\n}\n\nfunction getPathValue(prop: any): string | null {\n const key = prop.key\n if (!key) return null\n const keyName = key.type === 'Identifier' ? key.name : null\n if (keyName !== 'path') return null\n const val = prop.value\n if (val?.type === 'Literal' && typeof val.value === 'string') {\n return val.value\n }\n return null\n}\n\nfunction hasPathProperty(obj: any): boolean {\n if (!obj || obj.type !== 'ObjectExpression') return false\n for (const prop of obj.properties ?? []) {\n if (prop.type !== 'Property') continue\n if (getPathValue(prop) !== null) return true\n }\n return false\n}\n\nfunction hasCatchAllRoute(elements: any[]): boolean {\n for (const elem of elements) {\n if (!elem || elem.type !== 'ObjectExpression') continue\n for (const prop of elem.properties ?? []) {\n if (prop.type !== 'Property') continue\n const pathVal = getPathValue(prop)\n if (pathVal !== null && isCatchAllPath(pathVal)) return true\n }\n }\n return false\n}\n\nexport const noMissingFallback: Rule = {\n meta: {\n id: 'pyreon/no-missing-fallback',\n category: 'router',\n description:\n 'Warn when route config has no catch-all route (`path: \"*\"` or `path: \"/:rest*\"`).',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let importsRouter = false\n let routeArraySpan: { start: number; end: number } | null = null\n let foundCatchAll = false\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (info && info.source === '@pyreon/router') {\n importsRouter = true\n }\n },\n ArrayExpression(node: any) {\n if (!importsRouter) return\n const elements = node.elements ?? []\n const isRouteArray = elements.some((e: any) => hasPathProperty(e))\n if (!isRouteArray) return\n\n if (!routeArraySpan) {\n routeArraySpan = getSpan(node)\n }\n if (hasCatchAllRoute(elements)) {\n foundCatchAll = true\n }\n },\n 'Program:exit'() {\n if (!importsRouter || !routeArraySpan || foundCatchAll) return\n context.report({\n message:\n 'Route config has no catch-all route — add a `{ path: \"*\", component: NotFound }` for unmatched URLs.',\n span: routeArraySpan,\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const preferUseIsActive: Rule = {\n meta: {\n id: 'pyreon/prefer-use-is-active',\n category: 'router',\n description:\n 'Suggest useIsActive() instead of `location.pathname === \"/foo\"` or `route.path === \"/foo\"` patterns.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n BinaryExpression(node: any) {\n if (node.operator !== '===' && node.operator !== '==') return\n\n // Check both sides for location.pathname or route.path\n if (isPathComparison(node.left) || isPathComparison(node.right)) {\n context.report({\n message:\n 'Manual path comparison — use `useIsActive()` for reactive route matching with segment-aware prefix matching.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n\nfunction isPathComparison(node: any): boolean {\n if (!node || node.type !== 'MemberExpression') return false\n const obj = node.object\n const prop = node.property\n if (!obj || !prop || prop.type !== 'Identifier') return false\n\n // location.pathname\n if (obj.type === 'Identifier' && obj.name === 'location' && prop.name === 'pathname') return true\n\n // route.path\n if (obj.type === 'Identifier' && obj.name === 'route' && prop.name === 'path') return true\n\n return false\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isMemberCallTo } from '../../utils/ast'\n\nexport const noMismatchRisk: Rule = {\n meta: {\n id: 'pyreon/no-mismatch-risk',\n category: 'ssr',\n description:\n 'Warn about non-deterministic calls (Date.now, Math.random, crypto.randomUUID) in JSX context that cause hydration mismatches.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let jsxDepth = 0\n const callbacks: VisitorCallbacks = {\n JSXElement() {\n jsxDepth++\n },\n 'JSXElement:exit'() {\n jsxDepth--\n },\n JSXFragment() {\n jsxDepth++\n },\n 'JSXFragment:exit'() {\n jsxDepth--\n },\n CallExpression(node: any) {\n if (jsxDepth === 0) return\n\n if (\n isMemberCallTo(node, 'Date', 'now') ||\n isMemberCallTo(node, 'Math', 'random') ||\n isMemberCallTo(node, 'crypto', 'randomUUID')\n ) {\n const callee = node.callee\n const name = `${callee.object.name}.${callee.property.name}`\n context.report({\n message: `\\`${name}()\\` in JSX context — this produces different values on server and client, causing hydration mismatches.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\nimport { isPathExempt } from '../../utils/exempt-paths'\nimport { BROWSER_GLOBALS } from '../../utils/imports'\n\nexport const noWindowInSsr: Rule = {\n meta: {\n id: 'pyreon/no-window-in-ssr',\n category: 'ssr',\n description: 'Disallow browser globals outside onMount/effect/typeof guards — they break SSR.',\n severity: 'error',\n fixable: false,\n schema: { exemptPaths: 'string[]' },\n },\n create(context) {\n // Configurable `exemptPaths` option — projects opt out directories\n // that legitimately run in a DOM-only environment (e.g. a DOM renderer\n // package has no SSR scenario). Monorepo configures its own paths in\n // `.pyreonlintrc.json`; user apps typically leave this empty.\n if (isPathExempt(context)) return {}\n\n let safeDepth = 0\n let typeofGuardDepth = 0\n // Inside `typeof X` itself, the identifier mention is safe — `typeof` is\n // the only operator that doesn't evaluate its operand. We track this via\n // visitor enter/exit because the oxc visitor doesn't pass `parent` to\n // identifier callbacks (the previous `parent.operator === 'typeof'`\n // check was silently inert).\n let inTypeofExpr = 0\n // Identifiers that are member-property names (`x.addEventListener`),\n // object-property keys (`{ document: 1 }`), or import-specifier names\n // are NOT global references — pre-collected when their containing\n // node is visited, then skipped when the bare Identifier visitor fires.\n // Same root cause as `inTypeofExpr`: the previous `parent.type ===\n // 'MemberExpression'` check was inert (oxc visitor doesn't pass parent).\n const skipPropertyNodes = new WeakSet<any>()\n // Identifiers inside TypeScript type-position nodes (`let x: Window`,\n // `interface X { y: Document }`, `type T = Navigator`, generics, etc.)\n // are type references — they're erased at compile time. Track via depth\n // counter on any `TS*` node entry; identifiers visited while depth > 0\n // are skipped.\n let inTsTypePos = 0\n\n // Track `const isBrowser = typeof window !== 'undefined'` (or any\n // const whose initializer is a typeof check). Treats `if (isBrowser)`\n // the same as `if (typeof window !== 'undefined')` — the universal\n // browser-detection idiom. Set captured at module scope; lookups by name.\n const typeofBoundConsts = new Set<string>()\n function isPositiveTypeofCheck(expr: any): boolean {\n if (!expr) return false\n // `typeof X !== 'undefined'` (or `!=`) — the POSITIVE form: body is\n // the browser-safe branch.\n if (\n expr.type === 'BinaryExpression' &&\n (expr.operator === '!==' || expr.operator === '!=') &&\n expr.left?.type === 'UnaryExpression' &&\n expr.left.operator === 'typeof'\n )\n return true\n // Bare `typeof X` as truthiness check (rare).\n if (expr.type === 'UnaryExpression' && expr.operator === 'typeof') return true\n return false\n }\n /** Used by VariableDeclaration to decide whether to bind a const. */\n function isTypeofCheckForBinding(expr: any): boolean {\n if (!expr) return false\n if (\n expr.type === 'BinaryExpression' &&\n expr.left?.type === 'UnaryExpression' &&\n expr.left.operator === 'typeof'\n )\n return true\n if (expr.type === 'UnaryExpression' && expr.operator === 'typeof') return true\n // `const useVT = _isBrowser && ... && typeof X === 'function'` — if\n // any term in an AND chain is a typeof check (direct or via another\n // typeof-bound const), the whole expression is typeof-derived: every\n // non-falsy value requires every term to have evaluated truthy.\n if (expr.type === 'LogicalExpression' && expr.operator === '&&') {\n return isTypeofCheckForBinding(expr.left) || isTypeofCheckForBinding(expr.right)\n }\n // `const handler = _isBrowser ? (e) => … : null` / `_isBrowser ? fn()\n // : null` — ternary with a typeof-derived const as test. The non-null\n // branch only exists when the guard is truthy, so the binding is\n // transitively typeof-derived. Same for `_isBrowser ? X : null`\n // where `X` is typeof-derived.\n if (expr.type === 'ConditionalExpression') {\n return isTypeofCheckForBinding(expr.test)\n }\n if (expr.type === 'Identifier' && typeofBoundConsts.has(expr.name)) return true\n return false\n }\n /**\n * `if (test) { … }` — does the test indicate the body is the\n * BROWSER-SAFE branch? Only positive forms qualify here. Negated\n * forms (`typeof X === 'undefined'`, `!isBrowser`) are early-return\n * guards handled separately.\n */\n function testIsTypeofGuard(test: any): boolean {\n if (!test) return false\n if (isPositiveTypeofCheck(test)) return true\n // `if (isBrowser)` — bound from a typeof, body is browser-safe.\n if (test.type === 'Identifier' && typeofBoundConsts.has(test.name)) return true\n // `if (isBrowser())` — function whose body returns a typeof check.\n if (\n test.type === 'CallExpression' &&\n test.callee?.type === 'Identifier' &&\n typeofGuardFunctions.has(test.callee.name)\n )\n return true\n // `if (typeofGuard && other)` / `if (other && typeofGuard)` — either\n // side being a typeof guard means the body only runs when the guard\n // is truthy (AND short-circuits). Common in ternary tests like\n // `IS_BROWSER && active() ? <Portal … /> : null`.\n if (test.type === 'LogicalExpression' && test.operator === '&&') {\n return testIsTypeofGuard(test.left) || testIsTypeofGuard(test.right)\n }\n return false\n }\n // Functions whose body is `return <typeof check expr>` — invoked as\n // the convention `isBrowser()` / `isClient()` early-return guards.\n // E.g. `function isBrowser() { return typeof window !== 'undefined' }`.\n // Calls to these functions count as typeof checks for guard analysis.\n // Pre-seeded with conventional names (recognised across module\n // boundaries — same approach as `dev-guard-warnings` recognises\n // `__DEV__`/`IS_DEV`/etc. by name): user-supplied implementations of\n // `isBrowser` / `isClient` / `isServer` / `isSSR` are treated as\n // typeof guards even when imported from another file. Each file's\n // local `function isBrowser() { return typeof window !== 'undefined' }`\n // also adds itself to the set.\n const typeofGuardFunctions = new Set<string>(['isBrowser', 'isClient', 'isServer', 'isSSR'])\n function bodyIsTypeofGuard(body: any): boolean {\n if (!body) return false\n // Arrow concise body: `() => typeof window !== 'undefined'` →\n // body IS the expression directly, not a BlockStatement.\n if (body.type !== 'BlockStatement') return isReturnedTypeofExpr(body)\n // Block body: must be a single `return <expr>` statement.\n const stmts = body.body ?? []\n if (stmts.length !== 1) return false\n const stmt = stmts[0]\n if (stmt?.type !== 'ReturnStatement') return false\n return isReturnedTypeofExpr(stmt.argument)\n }\n function isReturnedTypeofExpr(expr: any): boolean {\n if (!expr) return false\n if (isPositiveTypeofCheck(expr)) return true\n // AND-chain of typeof checks (or typeof-bound consts) — a function\n // returning `typeof window !== 'undefined' && typeof document !== 'undefined'`\n // is still a typeof guard.\n if (expr.type === 'LogicalExpression' && expr.operator === '&&') {\n return isReturnedTypeofExpr(expr.left) && isReturnedTypeofExpr(expr.right)\n }\n // Identifier reference to a previously-bound typeof const.\n if (expr.type === 'Identifier' && typeofBoundConsts.has(expr.name)) return true\n return false\n }\n\n // Track functions that begin with an early-return on a NEGATED typeof\n // guard: `if (typeof window === 'undefined') return …` or\n // `if (!isBrowser) return`. After such a guard, the rest of the\n // function body is implicitly typeof-guarded (the SSR path bailed).\n // We use enter/exit on function nodes to bracket the guard zone.\n function isNegatedTypeofExpr(test: any): boolean {\n if (!test) return false\n // `typeof X === 'undefined'` — explicit equality form\n if (\n test.type === 'BinaryExpression' &&\n (test.operator === '===' || test.operator === '==') &&\n test.left?.type === 'UnaryExpression' &&\n test.left.operator === 'typeof'\n )\n return true\n // `!isBrowser` where isBrowser is bound from typeof\n if (\n test.type === 'UnaryExpression' &&\n test.operator === '!' &&\n test.argument?.type === 'Identifier' &&\n typeofBoundConsts.has(test.argument.name)\n )\n return true\n // `!isBrowser()` where isBrowser is a typeof-guard function — common\n // SSR pattern in storage adapters: `if (!isBrowser()) return null`.\n if (\n test.type === 'UnaryExpression' &&\n test.operator === '!' &&\n test.argument?.type === 'CallExpression' &&\n test.argument.callee?.type === 'Identifier' &&\n typeofGuardFunctions.has(test.argument.callee.name)\n )\n return true\n // `typeof X === 'undefined' || typeof Y === 'undefined'` — chained\n // SSR bailouts (common when a feature needs multiple browser APIs).\n // Both sides must be negated-typeof checks.\n if (test.type === 'LogicalExpression' && test.operator === '||') {\n return isNegatedTypeofExpr(test.left) && isNegatedTypeofExpr(test.right)\n }\n return false\n }\n function isEarlyReturnTypeofGuard(stmt: any): boolean {\n if (!stmt || stmt.type !== 'IfStatement') return false\n if (!isNegatedTypeofExpr(stmt.test)) return false\n // Consequent must terminate the function — either a return or a throw\n // (both bail out, leaving the rest of the body implicitly guarded).\n // Bare statement OR single-statement block are both accepted.\n const c = stmt.consequent\n const isTerminator = (s: any): boolean =>\n s?.type === 'ReturnStatement' || s?.type === 'ThrowStatement'\n if (isTerminator(c)) return true\n if (c?.type === 'BlockStatement' && c.body.length === 1 && isTerminator(c.body[0]))\n return true\n return false\n }\n // Per-function counter of how many typeofGuardDepth bumps were\n // contributed by early-return guards inside this function — popped on\n // function exit to keep the depth balanced.\n const earlyReturnStack: number[] = []\n // Callback nodes (2nd arg of `watch(source, cb)`) pre-marked so the\n // function-scope visitor bumps safeDepth only inside them. The source\n // arg (evaluated at setup) stays unmarked and gets normal analysis.\n const watchCallbackNodes = new WeakSet<any>()\n // Parallel stack recording whether the current function scope bumped\n // safeDepth for being a watch callback. Paired with `popFunctionScope`\n // so the depth is balanced even with nested watch calls.\n const watchCallbackSafeDepthStack: number[] = []\n // Stack of parameter names that shadow browser globals for the current\n // function scope. E.g. `function push(location)` — any `location`\n // identifier inside this function refers to the parameter, not the\n // browser global. Pushed on function enter, popped on exit.\n const shadowedNamesStack: Array<Set<string>> = []\n // Module-level names that shadow browser globals via imports — e.g.\n // `import { history } from '@codemirror/commands'`. Any `history`\n // identifier in the file then refers to the import, not `window.history`.\n // Populated by ImportSpecifier / ImportDefaultSpecifier / ImportNamespaceSpecifier.\n const importShadowedNames = new Set<string>()\n function collectParamNames(params: any[]): Set<string> {\n const names = new Set<string>()\n const walk = (p: any) => {\n if (!p) return\n if (p.type === 'Identifier' && BROWSER_GLOBALS.has(p.name)) names.add(p.name)\n else if (p.type === 'AssignmentPattern') walk(p.left)\n else if (p.type === 'RestElement') walk(p.argument)\n else if (p.type === 'ArrayPattern') for (const el of p.elements ?? []) walk(el)\n else if (p.type === 'ObjectPattern')\n for (const prop of p.properties ?? []) {\n if (prop.type === 'RestElement') walk(prop.argument)\n else walk(prop.value)\n }\n }\n for (const p of params ?? []) walk(p)\n return names\n }\n function isNameShadowed(name: string): boolean {\n for (let i = shadowedNamesStack.length - 1; i >= 0; i--) {\n if (shadowedNamesStack[i]!.has(name)) return true\n }\n return false\n }\n function pushFunctionScope(node?: any) {\n earlyReturnStack.push(0)\n shadowedNamesStack.push(node ? collectParamNames(node.params ?? []) : new Set())\n if (node && watchCallbackNodes.has(node)) {\n safeDepth++\n watchCallbackSafeDepthStack.push(1)\n } else {\n watchCallbackSafeDepthStack.push(0)\n }\n }\n function popFunctionScope() {\n const bumps = earlyReturnStack.pop() ?? 0\n typeofGuardDepth -= bumps\n shadowedNamesStack.pop()\n const watchBump = watchCallbackSafeDepthStack.pop() ?? 0\n if (watchBump > 0) safeDepth -= watchBump\n }\n function noteEarlyReturnGuardVisit() {\n typeofGuardDepth++\n if (earlyReturnStack.length > 0) {\n earlyReturnStack[earlyReturnStack.length - 1]!++\n }\n }\n\n const callbacks: VisitorCallbacks = {\n VariableDeclaration(node: any) {\n for (const decl of node.declarations ?? []) {\n if (decl.id?.type !== 'Identifier') continue\n // const isBrowser = typeof window !== 'undefined'\n if (isTypeofCheckForBinding(decl.init)) {\n typeofBoundConsts.add(decl.id.name)\n }\n // const isBrowser = () => typeof window !== 'undefined'\n // const isBrowser = function () { return typeof window !== 'undefined' }\n if (\n (decl.init?.type === 'ArrowFunctionExpression' ||\n decl.init?.type === 'FunctionExpression') &&\n bodyIsTypeofGuard(decl.init.body)\n ) {\n typeofGuardFunctions.add(decl.id.name)\n }\n }\n },\n FunctionDeclaration(node: any) {\n // function isBrowser() { return typeof window !== 'undefined' }\n if (node.id?.type === 'Identifier' && bodyIsTypeofGuard(node.body)) {\n typeofGuardFunctions.add(node.id.name)\n }\n pushFunctionScope(node)\n },\n 'FunctionDeclaration:exit': popFunctionScope,\n FunctionExpression: pushFunctionScope,\n 'FunctionExpression:exit': popFunctionScope,\n ArrowFunctionExpression: pushFunctionScope,\n 'ArrowFunctionExpression:exit': popFunctionScope,\n CallExpression(node: any) {\n // `onMount` / `onUnmount` / `onCleanup` / `effect` / `renderEffect` /\n // `requestAnimationFrame` — the whole call's arguments are safe:\n // the callback runs post-mount / in a browser frame, and those\n // hooks accept a single callback arg (no setup-time source).\n if (\n isCallTo(node, 'onMount') ||\n isCallTo(node, 'onUnmount') ||\n isCallTo(node, 'onCleanup') ||\n isCallTo(node, 'effect') ||\n isCallTo(node, 'renderEffect') ||\n isCallTo(node, 'requestAnimationFrame')\n ) {\n safeDepth++\n return\n }\n // `watch(source, cb)` — the SOURCE arg is evaluated synchronously\n // at setup time (to track signals) so browser-global access inside\n // it is NOT safe. Only the CALLBACK arg fires deferred. Pre-mark\n // the second arg so the ArrowFn/FunctionExpression visitor bumps\n // safeDepth only there — not across the whole CallExpression.\n if (isCallTo(node, 'watch')) {\n const cb = node.arguments?.[1]\n if (\n cb?.type === 'ArrowFunctionExpression' ||\n cb?.type === 'FunctionExpression' ||\n cb?.type === 'FunctionDeclaration'\n ) {\n watchCallbackNodes.add(cb)\n }\n }\n },\n 'CallExpression:exit'(node: any) {\n if (\n isCallTo(node, 'onMount') ||\n isCallTo(node, 'onUnmount') ||\n isCallTo(node, 'onCleanup') ||\n isCallTo(node, 'effect') ||\n isCallTo(node, 'renderEffect') ||\n isCallTo(node, 'requestAnimationFrame')\n ) {\n safeDepth--\n }\n },\n IfStatement(node: any) {\n // `if (typeof X !== 'undefined') { … browser-only … }` —\n // body-scoped typeof guard.\n if (testIsTypeofGuard(node.test)) typeofGuardDepth++\n // `if (typeof X === 'undefined') return` — early-return guard.\n // Bumps the guard depth FROM HERE through the rest of the\n // enclosing function (popped at function exit). Done at IfStatement\n // visit (not function enter) so `typeofBoundConsts` is already\n // populated by any `const isBrowser = …` above this if.\n else if (isEarlyReturnTypeofGuard(node)) noteEarlyReturnGuardVisit()\n },\n 'IfStatement:exit'(node: any) {\n if (testIsTypeofGuard(node.test)) typeofGuardDepth--\n },\n // Ternary `typeof X !== 'undefined' ? safe : fallback` — the\n // `consequent` branch is type-guarded. Tracked via depth: enter\n // increments globally because the visitor doesn't tell us which\n // branch we're in. That's an over-approximation (the fallback also\n // gets the depth bump), but the fallback typically doesn't reference\n // browser globals; the consequent does. Conservative; matches real\n // code patterns.\n ConditionalExpression(node: any) {\n if (testIsTypeofGuard(node.test)) typeofGuardDepth++\n },\n 'ConditionalExpression:exit'(node: any) {\n if (testIsTypeofGuard(node.test)) typeofGuardDepth--\n },\n UnaryExpression(node: any) {\n if (node.operator === 'typeof') inTypeofExpr++\n },\n 'UnaryExpression:exit'(node: any) {\n if (node.operator === 'typeof') inTypeofExpr--\n },\n // TypeScript type-position nodes — identifiers inside these are\n // type references (erased at compile), not runtime accesses. Cover\n // the common entry points; depth counter handles any nested\n // `TSTypeAnnotation` etc.\n TSTypeAnnotation(_n: any) { inTsTypePos++ },\n 'TSTypeAnnotation:exit'(_n: any) { inTsTypePos-- },\n TSTypeReference(_n: any) { inTsTypePos++ },\n 'TSTypeReference:exit'(_n: any) { inTsTypePos-- },\n TSTypeAliasDeclaration(_n: any) { inTsTypePos++ },\n 'TSTypeAliasDeclaration:exit'(_n: any) { inTsTypePos-- },\n TSInterfaceDeclaration(_n: any) { inTsTypePos++ },\n 'TSInterfaceDeclaration:exit'(_n: any) { inTsTypePos-- },\n TSTypeParameter(_n: any) { inTsTypePos++ },\n 'TSTypeParameter:exit'(_n: any) { inTsTypePos-- },\n MemberExpression(node: any) {\n // `x.addEventListener` — `addEventListener` is the property name, not\n // a global. Pre-mark so the Identifier visitor skips it.\n if (!node.computed && node.property?.type === 'Identifier') {\n skipPropertyNodes.add(node.property)\n }\n },\n Property(node: any) {\n // `{ document: 1 }` — `document` is a key, not a global ref.\n if (!node.computed && node.key?.type === 'Identifier') {\n skipPropertyNodes.add(node.key)\n }\n },\n ImportSpecifier(node: any) {\n if (node.imported?.type === 'Identifier') skipPropertyNodes.add(node.imported)\n if (node.local?.type === 'Identifier' && node.local !== node.imported)\n skipPropertyNodes.add(node.local)\n // Track imported names that shadow a browser global so all later\n // uses of that name in the file are skipped — e.g. `import { history }\n // from '@codemirror/commands'` makes every `history` identifier a\n // CodeMirror reference, not `window.history`.\n if (node.local?.type === 'Identifier' && BROWSER_GLOBALS.has(node.local.name)) {\n importShadowedNames.add(node.local.name)\n }\n },\n ImportDefaultSpecifier(node: any) {\n if (node.local?.type === 'Identifier') {\n skipPropertyNodes.add(node.local)\n if (BROWSER_GLOBALS.has(node.local.name)) importShadowedNames.add(node.local.name)\n }\n },\n ImportNamespaceSpecifier(node: any) {\n if (node.local?.type === 'Identifier') {\n skipPropertyNodes.add(node.local)\n if (BROWSER_GLOBALS.has(node.local.name)) importShadowedNames.add(node.local.name)\n }\n },\n Identifier(node: any) {\n if (safeDepth > 0 || typeofGuardDepth > 0 || inTypeofExpr > 0 || inTsTypePos > 0) return\n if (skipPropertyNodes.has(node)) return\n if (!BROWSER_GLOBALS.has(node.name)) return\n // Skip identifiers shadowed by a parameter of the same name —\n // `function push(location)` inside: every `location` refers to the\n // parameter, not `window.location`.\n if (isNameShadowed(node.name)) return\n // Skip identifiers shadowed by a module-level import binding.\n if (importShadowedNames.has(node.name)) return\n\n context.report({\n message: `Browser global \\`${node.name}\\` used outside \\`onMount\\`/\\`effect\\`/typeof guard — this will fail during SSR. Wrap in \\`onMount(() => { ... })\\`.`,\n span: getSpan(node),\n })\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\n\nexport const preferRequestContext: Rule = {\n meta: {\n id: 'pyreon/prefer-request-context',\n category: 'ssr',\n description:\n 'Warn about module-level signal()/createStore() in server files — use request context instead.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const filePath = context.getFilePath()\n const isServerFile =\n filePath.includes('server') ||\n filePath.includes('.server.') ||\n filePath.endsWith('server.ts') ||\n filePath.endsWith('server.tsx')\n\n if (!isServerFile) return {}\n\n let functionDepth = 0\n const callbacks: VisitorCallbacks = {\n FunctionDeclaration() {\n functionDepth++\n },\n 'FunctionDeclaration:exit'() {\n functionDepth--\n },\n FunctionExpression() {\n functionDepth++\n },\n 'FunctionExpression:exit'() {\n functionDepth--\n },\n ArrowFunctionExpression() {\n functionDepth++\n },\n 'ArrowFunctionExpression:exit'() {\n functionDepth--\n },\n CallExpression(node: any) {\n if (functionDepth > 0) return // only flag module-level calls\n if (isCallTo(node, 'signal') || isCallTo(node, 'createStore')) {\n const name = node.callee.name\n context.report({\n message: `Module-level \\`${name}()\\` in a server file — this state is shared across all requests. Use \\`runWithRequestContext()\\` for per-request isolation.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\nimport { isTestFile } from '../../utils/file-roles'\n\nexport const noDuplicateStoreId: Rule = {\n meta: {\n id: 'pyreon/no-duplicate-store-id',\n category: 'store',\n description: 'Disallow duplicate defineStore() IDs in the same file.',\n severity: 'error',\n fixable: false,\n },\n create(context) {\n // Heuristic: skip test files. The rule catches a real bug (two\n // `defineStore('foo', ...)` calls in production code clobber each\n // other), but store tests deliberately duplicate IDs to assert\n // collision-handling behavior. A truly precise check would need to\n // detect \"this duplicate is wrapped in `expect(...).toThrow`\" or\n // similar — impractical at lint level. For prod code that intentionally\n // (re)defines a store ID, use `// pyreon-lint-disable-next-line` at\n // the second declaration.\n if (isTestFile(context.getFilePath())) return {}\n\n const storeIds = new Map<string, { start: number; end: number }>()\n\n const callbacks: VisitorCallbacks = {\n CallExpression(node: any) {\n if (!isCallTo(node, 'defineStore')) return\n const args = node.arguments\n if (!args || args.length === 0) return\n\n const firstArg = args[0]\n if (!firstArg) return\n\n let id: string | null = null\n if (firstArg.type === 'Literal' || firstArg.type === 'StringLiteral') {\n id = firstArg.value as string\n }\n\n if (typeof id !== 'string') return\n\n if (storeIds.has(id)) {\n context.report({\n message: `Duplicate store ID \\`\"${id}\"\\` — each \\`defineStore()\\` must have a unique ID.`,\n span: getSpan(node),\n })\n } else {\n storeIds.set(id, getSpan(node))\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { createComponentContextTracker } from '../../utils/component-context'\n\nexport const noMutateStoreState: Rule = {\n meta: {\n id: 'pyreon/no-mutate-store-state',\n category: 'store',\n description:\n 'Warn when calling .set() on store signals from a component or hook — use store actions instead.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n // The wrong pattern is mutating store state from a component / event\n // handler / hook. Inside the store's own setup or in tests asserting\n // reactivity, `.set()` is fine. Component-context detection naturally\n // skips both cases without a path-based heuristic.\n const ctx = createComponentContextTracker()\n\n const callbacks: VisitorCallbacks = {\n ...ctx.callbacks,\n CallExpression(node: any) {\n if (!ctx.isInComponentOrHook()) return\n const callee = node.callee\n if (!callee || callee.type !== 'MemberExpression') return\n if (callee.property?.type !== 'Identifier' || callee.property.name !== 'set') return\n\n // Check for store.signal.set() pattern — member.member.set()\n const obj = callee.object\n if (!obj || obj.type !== 'MemberExpression') return\n const outerObj = obj.object\n if (!outerObj || outerObj.type !== 'Identifier') return\n\n const name: string = outerObj.name\n // Heuristic: if the outer object name contains \"store\" (case-insensitive)\n if (name.toLowerCase().includes('store')) {\n context.report({\n message: `Direct \\`.set()\\` on store state \\`${name}\\` — use store actions to mutate state for better traceability.`,\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\nimport { extractImportInfo } from '../../utils/imports'\n\nexport const noStoreOutsideProvider: Rule = {\n meta: {\n id: 'pyreon/no-store-outside-provider',\n category: 'store',\n description: 'Warn when store hooks are used in SSR files without a provider import.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const filePath = context.getFilePath()\n const isServerFile =\n filePath.includes('server') ||\n filePath.includes('.server.') ||\n filePath.endsWith('server.ts') ||\n filePath.endsWith('server.tsx')\n\n if (!isServerFile) return {}\n\n let hasProviderImport = false\n const storeHookCalls: Array<{ name: string; span: { start: number; end: number } }> = []\n\n const callbacks: VisitorCallbacks = {\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (!info) return\n if (\n info.specifiers.some(\n (s) =>\n s.imported === 'setStoreRegistryProvider' || s.imported === 'runWithRequestContext',\n )\n ) {\n hasProviderImport = true\n }\n },\n CallExpression(node: any) {\n const callee = node.callee\n if (!callee || callee.type !== 'Identifier') return\n const name: string = callee.name\n if (name.endsWith('Store') && name.startsWith('use')) {\n storeHookCalls.push({ name, span: getSpan(node) })\n }\n },\n 'Program:exit'() {\n if (hasProviderImport) return\n for (const call of storeHookCalls) {\n context.report({\n message: `\\`${call.name}()\\` in a server file without a store registry provider — use \\`runWithRequestContext()\\` or \\`setStoreRegistryProvider()\\` for SSR isolation.`,\n span: call.span,\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\nimport { createComponentContextTracker } from '../../utils/component-context'\n\nexport const noDynamicStyled: Rule = {\n meta: {\n id: 'pyreon/no-dynamic-styled',\n category: 'styling',\n description:\n 'Warn when styled() is called inside a component or hook — it creates new CSS on every render.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n // Only flag when *inside* a component / hook setup body. Module-level\n // `styled()` is the correct pattern. Inside a utility function, factory,\n // or test callback `styled()` runs once per call but isn't tied to a\n // render path — the per-render-allocation warning doesn't apply.\n const ctx = createComponentContextTracker()\n\n const callbacks: VisitorCallbacks = {\n ...ctx.callbacks,\n CallExpression(node: any) {\n if (!ctx.isInComponentOrHook()) return\n if (isCallTo(node, 'styled')) {\n context.report({\n message:\n '`styled()` inside a component or hook — this creates new CSS rules on every render. Move `styled()` to module scope.',\n span: getSpan(node),\n })\n }\n },\n TaggedTemplateExpression(node: any) {\n if (!ctx.isInComponentOrHook()) return\n const tag = node.tag\n if (!tag) return\n // styled('div')`...` — tag is a CallExpression of styled\n if (tag.type === 'CallExpression' && isCallTo(tag, 'styled')) {\n context.report({\n message:\n '`styled()` tagged template inside a component or hook — this creates new CSS rules on every render. Move to module scope.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const noInlineStyleObject: Rule = {\n meta: {\n id: 'pyreon/no-inline-style-object',\n category: 'styling',\n description: 'Warn against inline style objects in JSX — prefer styled() or css``.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXAttribute(node: any) {\n if (node.name?.type !== 'JSXIdentifier' || node.name.name !== 'style') return\n const value = node.value\n if (!value || value.type !== 'JSXExpressionContainer') return\n const expr = value.expression\n if (expr?.type === 'ObjectExpression') {\n context.report({\n message:\n 'Inline style object in JSX — consider using `styled()` or `css\\\\`...\\\\`` for better performance and caching.',\n span: getSpan(node),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan, isCallTo } from '../../utils/ast'\nimport { extractImportInfo } from '../../utils/imports'\n\nconst HOOK_NAME = /^use[A-Z]/\n\nexport const noThemeOutsideProvider: Rule = {\n meta: {\n id: 'pyreon/no-theme-outside-provider',\n category: 'styling',\n description: 'Warn when useTheme() is used without PyreonUI or ThemeProvider in the same file.',\n severity: 'warn',\n fixable: false,\n },\n create(context) {\n let hasProviderImport = false\n let hookDepth = 0\n // Each useTheme call records whether it was inside a hook implementation.\n // Hook files (e.g. `useThemeValue.ts`) legitimately call `useTheme()` to\n // re-export theme access — they delegate provider responsibility to the\n // consuming component, which is the place the rule actually targets.\n const themeCalls: Array<{ span: { start: number; end: number }; insideHook: boolean }> = []\n\n function declaratorIsHook(node: any): boolean {\n if (node?.id?.type !== 'Identifier') return false\n const init = node.init\n if (init?.type !== 'ArrowFunctionExpression' && init?.type !== 'FunctionExpression') return false\n return HOOK_NAME.test(node.id.name)\n }\n\n const callbacks: VisitorCallbacks = {\n // `function useFoo() { … }`\n FunctionDeclaration(node: any) {\n if (node.id?.name && HOOK_NAME.test(node.id.name)) hookDepth++\n },\n 'FunctionDeclaration:exit'(node: any) {\n if (node.id?.name && HOOK_NAME.test(node.id.name)) hookDepth--\n },\n // `const useFoo = () => { … }` (oxc visitor doesn't pass parent, so\n // hook into the declarator — it brackets the inner function body).\n VariableDeclarator(node: any) {\n if (declaratorIsHook(node)) hookDepth++\n },\n 'VariableDeclarator:exit'(node: any) {\n if (declaratorIsHook(node)) hookDepth--\n },\n ImportDeclaration(node: any) {\n const info = extractImportInfo(node)\n if (!info) return\n if (\n info.specifiers.some((s) => s.imported === 'PyreonUI' || s.imported === 'ThemeProvider')\n ) {\n hasProviderImport = true\n }\n },\n CallExpression(node: any) {\n if (isCallTo(node, 'useTheme')) {\n themeCalls.push({ span: getSpan(node), insideHook: hookDepth > 0 })\n }\n },\n 'Program:exit'() {\n if (hasProviderImport) return\n for (const call of themeCalls) {\n // Inside a hook implementation? The hook delegates provider\n // responsibility to its caller — silence here.\n if (call.insideHook) continue\n context.report({\n message:\n '`useTheme()` without a `PyreonUI` or `ThemeProvider` import — the theme context may not be available.',\n span: call.span,\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule, VisitorCallbacks } from '../../types'\nimport { getSpan } from '../../utils/ast'\n\nexport const preferCx: Rule = {\n meta: {\n id: 'pyreon/prefer-cx',\n category: 'styling',\n description:\n 'Suggest cx() for class composition instead of string concatenation or template literals.',\n severity: 'info',\n fixable: false,\n },\n create(context) {\n const callbacks: VisitorCallbacks = {\n JSXAttribute(node: any) {\n if (node.name?.type !== 'JSXIdentifier' || node.name.name !== 'class') return\n const value = node.value\n if (!value || value.type !== 'JSXExpressionContainer') return\n const expr = value.expression\n if (!expr) return\n\n // String concatenation: \"foo \" + bar\n if (expr.type === 'BinaryExpression' && expr.operator === '+') {\n context.report({\n message:\n 'String concatenation in `class` attribute — use `cx()` for cleaner class composition.',\n span: getSpan(expr),\n })\n return\n }\n\n // Template literal: `foo ${bar}`\n if (expr.type === 'TemplateLiteral' && expr.expressions?.length > 0) {\n context.report({\n message:\n 'Template literal in `class` attribute — use `cx()` for cleaner class composition.',\n span: getSpan(expr),\n })\n }\n },\n }\n return callbacks\n },\n}\n","import type { Rule } from '../types'\nimport { dialogA11y } from './accessibility/dialog-a11y'\nimport { overlayA11y } from './accessibility/overlay-a11y'\n// Accessibility\nimport { toastA11y } from './accessibility/toast-a11y'\nimport { devGuardWarnings } from './architecture/dev-guard-warnings'\n// Architecture\nimport { noCircularImport } from './architecture/no-circular-import'\nimport { noCrossLayerImport } from './architecture/no-cross-layer-import'\nimport { noDeepImport } from './architecture/no-deep-import'\nimport { noErrorWithoutPrefix } from './architecture/no-error-without-prefix'\nimport { noProcessDevGate } from './architecture/no-process-dev-gate'\nimport { requireBrowserSmokeTest } from './architecture/require-browser-smoke-test'\nimport { noSubmitWithoutValidation } from './form/no-submit-without-validation'\n// Form\nimport { noUnregisteredField } from './form/no-unregistered-field'\nimport { preferFieldArray } from './form/prefer-field-array'\n// Hooks\nimport { noRawAddEventListener } from './hooks/no-raw-addeventlistener'\nimport { noRawLocalStorage } from './hooks/no-raw-localstorage'\nimport { noRawSetInterval } from './hooks/no-raw-setinterval'\nimport { noAndConditional } from './jsx/no-and-conditional'\nimport { noChildrenAccess } from './jsx/no-children-access'\nimport { noClassName } from './jsx/no-classname'\nimport { noHtmlFor } from './jsx/no-htmlfor'\nimport { noIndexAsBy } from './jsx/no-index-as-by'\n// JSX\nimport { noMapInJsx } from './jsx/no-map-in-jsx'\nimport { noMissingForBy } from './jsx/no-missing-for-by'\nimport { noOnChange } from './jsx/no-onchange'\nimport { noPropsDestructure } from './jsx/no-props-destructure'\nimport { noTernaryConditional } from './jsx/no-ternary-conditional'\nimport { useByNotKey } from './jsx/use-by-not-key'\nimport { noDomInSetup } from './lifecycle/no-dom-in-setup'\nimport { noEffectInMount } from './lifecycle/no-effect-in-mount'\n// Lifecycle\nimport { noMissingCleanup } from './lifecycle/no-missing-cleanup'\nimport { noMountInEffect } from './lifecycle/no-mount-in-effect'\nimport { noEagerImport } from './performance/no-eager-import'\nimport { noEffectInFor } from './performance/no-effect-in-for'\n// Performance\nimport { noLargeForWithoutBy } from './performance/no-large-for-without-by'\nimport { preferShowOverDisplay } from './performance/prefer-show-over-display'\n// Reactivity\nimport { noBareSignalInJsx } from './reactivity/no-bare-signal-in-jsx'\nimport { noContextDestructure } from './reactivity/no-context-destructure'\nimport { noEffectAssignment } from './reactivity/no-effect-assignment'\nimport { noNestedEffect } from './reactivity/no-nested-effect'\nimport { noPeekInTracked } from './reactivity/no-peek-in-tracked'\nimport { noSignalInLoop } from './reactivity/no-signal-in-loop'\nimport { noSignalInProps } from './reactivity/no-signal-in-props'\nimport { noSignalLeak } from './reactivity/no-signal-leak'\nimport { noUnbatchedUpdates } from './reactivity/no-unbatched-updates'\nimport { preferComputed } from './reactivity/prefer-computed'\n// Router\nimport { noHrefNavigation } from './router/no-href-navigation'\nimport { noImperativeNavigateInRender } from './router/no-imperative-navigate-in-render'\nimport { noMissingFallback } from './router/no-missing-fallback'\nimport { preferUseIsActive } from './router/prefer-use-is-active'\nimport { noMismatchRisk } from './ssr/no-mismatch-risk'\n// SSR\nimport { noWindowInSsr } from './ssr/no-window-in-ssr'\nimport { preferRequestContext } from './ssr/prefer-request-context'\nimport { noDuplicateStoreId } from './store/no-duplicate-store-id'\nimport { noMutateStoreState } from './store/no-mutate-store-state'\n// Store\nimport { noStoreOutsideProvider } from './store/no-store-outside-provider'\nimport { noDynamicStyled } from './styling/no-dynamic-styled'\n// Styling\nimport { noInlineStyleObject } from './styling/no-inline-style-object'\nimport { noThemeOutsideProvider } from './styling/no-theme-outside-provider'\nimport { preferCx } from './styling/prefer-cx'\n\nexport const allRules: Rule[] = [\n // Reactivity (10)\n noBareSignalInJsx,\n noContextDestructure,\n noSignalInLoop,\n noSignalInProps,\n noNestedEffect,\n noPeekInTracked,\n noUnbatchedUpdates,\n preferComputed,\n noEffectAssignment,\n noSignalLeak,\n // JSX (11)\n noMapInJsx,\n useByNotKey,\n noClassName,\n noHtmlFor,\n noOnChange,\n noTernaryConditional,\n noAndConditional,\n noIndexAsBy,\n noMissingForBy,\n noPropsDestructure,\n noChildrenAccess,\n // Lifecycle (4)\n noMissingCleanup,\n noMountInEffect,\n noEffectInMount,\n noDomInSetup,\n // Performance (4)\n noLargeForWithoutBy,\n noEffectInFor,\n noEagerImport,\n preferShowOverDisplay,\n // SSR (3)\n noWindowInSsr,\n noMismatchRisk,\n preferRequestContext,\n // Architecture (7)\n noCircularImport,\n noDeepImport,\n noCrossLayerImport,\n devGuardWarnings,\n noErrorWithoutPrefix,\n noProcessDevGate,\n requireBrowserSmokeTest,\n // Store (3)\n noStoreOutsideProvider,\n noMutateStoreState,\n noDuplicateStoreId,\n // Form (3)\n noUnregisteredField,\n noSubmitWithoutValidation,\n preferFieldArray,\n // Styling (4)\n noInlineStyleObject,\n noDynamicStyled,\n preferCx,\n noThemeOutsideProvider,\n // Hooks (3)\n noRawAddEventListener,\n noRawSetInterval,\n noRawLocalStorage,\n // Accessibility (3)\n toastA11y,\n dialogA11y,\n overlayA11y,\n // Router (4)\n noHrefNavigation,\n noImperativeNavigateInRender,\n noMissingFallback,\n preferUseIsActive,\n]\n\n// Re-export all rules individually\nexport {\n devGuardWarnings,\n dialogA11y,\n noAndConditional,\n // Reactivity\n noBareSignalInJsx,\n noContextDestructure,\n noChildrenAccess,\n // Architecture\n noCircularImport,\n noClassName,\n noCrossLayerImport,\n noDeepImport,\n noDomInSetup,\n noDuplicateStoreId,\n noDynamicStyled,\n noEagerImport,\n noEffectAssignment,\n noEffectInFor,\n noEffectInMount,\n noErrorWithoutPrefix,\n noHrefNavigation,\n noHtmlFor,\n noImperativeNavigateInRender,\n noIndexAsBy,\n // Styling\n noInlineStyleObject,\n // Performance\n noLargeForWithoutBy,\n // JSX\n noMapInJsx,\n noMismatchRisk,\n // Lifecycle\n noMissingCleanup,\n noMissingFallback,\n noMissingForBy,\n noMountInEffect,\n noMutateStoreState,\n noNestedEffect,\n noOnChange,\n noPeekInTracked,\n noProcessDevGate,\n noPropsDestructure,\n requireBrowserSmokeTest,\n // Hooks\n noRawAddEventListener,\n noRawLocalStorage,\n noRawSetInterval,\n noSignalInLoop,\n noSignalInProps,\n noSignalLeak,\n // Store\n noStoreOutsideProvider,\n noSubmitWithoutValidation,\n noTernaryConditional,\n noThemeOutsideProvider,\n noUnbatchedUpdates,\n // Form\n noUnregisteredField,\n // SSR\n noWindowInSsr,\n overlayA11y,\n preferComputed,\n preferCx,\n preferFieldArray,\n preferRequestContext,\n preferShowOverDisplay,\n preferUseIsActive,\n // Accessibility\n toastA11y,\n useByNotKey,\n}\n","import { allRules } from '../rules/index'\nimport type { LintConfig, PresetName, Severity } from '../types'\n\n/** Build a config where every rule uses its default severity. */\nfunction buildRecommended(): LintConfig {\n const rules: Record<string, Severity> = {}\n for (const rule of allRules) {\n rules[rule.meta.id] = rule.meta.severity\n }\n return { rules }\n}\n\nfunction severityOf(entry: LintConfig['rules'][string]): Severity {\n // Presets are built from bare severities (no tuple form). If a future\n // preset adds tuple form, extract the severity from the tuple.\n return Array.isArray(entry) ? (entry[0] as Severity) : (entry as Severity)\n}\n\n/** Build a config where every warn is promoted to error. */\nfunction buildStrict(): LintConfig {\n const base = buildRecommended()\n const rules: Record<string, Severity> = {}\n for (const [id, entry] of Object.entries(base.rules)) {\n const sev = severityOf(entry)\n rules[id] = sev === 'warn' ? 'error' : sev\n }\n return { rules }\n}\n\n/** Build app config — recommended but disable library-only rules. */\nfunction buildApp(): LintConfig {\n const base = buildRecommended()\n return {\n rules: {\n ...base.rules,\n 'pyreon/dev-guard-warnings': 'off',\n 'pyreon/no-error-without-prefix': 'off',\n 'pyreon/no-circular-import': 'off',\n 'pyreon/no-cross-layer-import': 'off',\n // `require-browser-smoke-test` is a per-package contract that\n // applies to published libraries — apps don't ship as packages\n // with smoke obligations.\n 'pyreon/require-browser-smoke-test': 'off',\n // `no-process-dev-gate` stays ON in `app` preset because the bug\n // hits user-facing browser code regardless of whether it's a lib\n // or an app.\n },\n }\n}\n\n/** Build lib config — strict + all architecture rules as error. */\nfunction buildLib(): LintConfig {\n const base = buildStrict()\n return {\n rules: {\n ...base.rules,\n 'pyreon/no-circular-import': 'error',\n 'pyreon/no-cross-layer-import': 'error',\n 'pyreon/dev-guard-warnings': 'error',\n 'pyreon/no-error-without-prefix': 'error',\n 'pyreon/no-process-dev-gate': 'error',\n 'pyreon/require-browser-smoke-test': 'error',\n },\n }\n}\n\nconst presetBuilders: Record<PresetName, () => LintConfig> = {\n recommended: buildRecommended,\n strict: buildStrict,\n app: buildApp,\n lib: buildLib,\n}\n\nexport function getPreset(name: PresetName): LintConfig {\n return presetBuilders[name]()\n}\n\nexport { buildApp, buildLib, buildRecommended, buildStrict }\n","import type { SourceLocation } from '../types'\n\n/**\n * Fast offset→line/column conversion using binary search over precomputed line starts.\n */\nexport class LineIndex {\n private lineStarts: number[]\n\n constructor(sourceText: string) {\n this.lineStarts = [0]\n for (let i = 0; i < sourceText.length; i++) {\n if (sourceText[i] === '\\n') {\n this.lineStarts.push(i + 1)\n }\n }\n }\n\n /** Convert a byte offset to a 1-based line and 0-based column. */\n locate(offset: number): SourceLocation {\n let lo = 0\n let hi = this.lineStarts.length - 1\n\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1\n if ((this.lineStarts[mid] as number) <= offset) {\n lo = mid + 1\n } else {\n hi = mid - 1\n }\n }\n\n const line = lo // 1-based (lo points one past the found index)\n const column = offset - (this.lineStarts[line - 1] as number)\n return { line, column }\n }\n}\n","export {\n getJSXAttribute,\n getJSXTagName,\n getSpan,\n hasJSXAttribute,\n hasJSXChild,\n isArrayMapCall,\n isBrowserGlobal,\n isCallTo,\n isCallToAny,\n isDestructuring,\n isFunction,\n isInsideDevGuard,\n isInsideFunction,\n isInsideJSX,\n isInsideOnMount,\n isInsideTypeofGuard,\n isJSXElement,\n isLogicalAndWithJSX,\n isMemberCallTo,\n isPeekCall,\n isSetCall,\n isTernaryWithJSX,\n} from './ast'\nexport {\n BROWSER_GLOBALS,\n CONTEXT_APIS,\n extractImportInfo,\n getLocalName,\n HEAVY_PACKAGES,\n importsName,\n isPyreonImport,\n isPyreonPackage,\n JSX_COMPONENTS,\n LIFECYCLE_APIS,\n PYREON_PREFIX,\n REACTIVITY_APIS,\n} from './imports'\nexport { LineIndex } from './source'\n\n/** Supported JS/TS file extensions for linting. */\nexport const JS_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mts', '.mjs'])\n\n/** Check if a file path has a supported JS/TS extension. */\nexport function hasJsExtension(filePath: string): boolean {\n const ext = filePath.slice(filePath.lastIndexOf('.'))\n return JS_EXTENSIONS.has(ext)\n}\n","/**\n * Validate a rule's user-configured options against its declared schema.\n *\n * Called once per (rule, options) pair at config-merge time — NOT per\n * lint'd file. Separates config problems from source-code problems:\n * wrong-typed options aren't a file diagnostic, they're a setup error\n * for the tool.\n *\n * Return shape:\n * - `errors`: hard failures. Runner disables the rule for this run.\n * - `warnings`: unknown option keys, typos, etc. Runner keeps the\n * rule enabled but prints the warning so the user knows.\n *\n * Rules without `meta.schema` accept any options (no validation).\n */\n\nimport type { OptionType, Rule, RuleOptions, RuleOptionsSchema } from '../types'\n\nexport interface ValidationResult {\n errors: string[]\n warnings: string[]\n}\n\nexport function validateRuleOptions(rule: Rule, options: RuleOptions): ValidationResult {\n const schema = rule.meta.schema\n const errors: string[] = []\n const warnings: string[] = []\n if (!schema) return { errors, warnings }\n\n for (const [key, value] of Object.entries(options)) {\n const expected = schema[key]\n if (expected === undefined) {\n warnings.push(\n `[${rule.meta.id}] unknown option \"${key}\" — allowed options: ${Object.keys(schema).join(', ') || '(none)'}`,\n )\n continue\n }\n if (!matchesType(value, expected)) {\n errors.push(\n `[${rule.meta.id}] option \"${key}\" must be ${expected}, got ${describe(value)}`,\n )\n }\n }\n\n return { errors, warnings }\n}\n\nfunction matchesType(value: unknown, type: OptionType): boolean {\n switch (type) {\n case 'string':\n return typeof value === 'string'\n case 'string[]':\n return Array.isArray(value) && value.every((x) => typeof x === 'string')\n case 'number':\n return typeof value === 'number' && Number.isFinite(value)\n case 'boolean':\n return typeof value === 'boolean'\n }\n}\n\nfunction describe(value: unknown): string {\n if (value === null) return 'null'\n if (Array.isArray(value)) {\n const types = new Set(value.map((x) => (x === null ? 'null' : typeof x)))\n return `Array<${[...types].join(' | ') || 'empty'}>`\n }\n return typeof value\n}\n","import { parseSync, Visitor } from 'oxc-parser'\nimport type { AstCache } from './cache'\nimport type {\n ConfigDiagnostic,\n Diagnostic,\n LintConfig,\n LintFileResult,\n Rule,\n RuleContext,\n RuleOptions,\n Severity,\n VisitorCallbacks,\n} from './types'\nimport { JS_EXTENSIONS } from './utils/index'\nimport { LineIndex } from './utils/source'\nimport { validateRuleOptions } from './utils/validate-options'\n\n// Per-process cache so we only validate a given (rule, options) pair once\n// and only print-once even across a multi-file lint run.\nconst VALIDATION_CACHE = new Map<string, { ok: boolean; diagnostics: ConfigDiagnostic[] }>()\n\n/** Reset caches — exposed for tests; not part of the public surface. */\nexport function _resetConfigDiagnosticsCache(): void {\n VALIDATION_CACHE.clear()\n}\n\nfunction getExtension(filePath: string): string {\n const lastDot = filePath.lastIndexOf('.')\n return lastDot === -1 ? '' : filePath.slice(lastDot)\n}\n\ntype OxcLang = 'jsx' | 'tsx' | 'ts' | 'js' | 'dts'\n\nfunction getLang(ext: string): OxcLang {\n if (ext === '.tsx' || ext === '.jsx') return 'tsx'\n if (ext === '.ts' || ext === '.mts') return 'ts'\n return 'js'\n}\n\nfunction createRuleContext(\n rule: Rule,\n severity: Severity,\n options: RuleOptions,\n diagnostics: Diagnostic[],\n lineIndex: LineIndex,\n sourceText: string,\n filePath: string,\n): RuleContext {\n return {\n report(partial) {\n diagnostics.push({\n ruleId: rule.meta.id,\n severity,\n message: partial.message,\n span: partial.span,\n loc: lineIndex.locate(partial.span.start),\n fix: partial.fix,\n })\n },\n getSourceText() {\n return sourceText\n },\n getFilePath() {\n return filePath\n },\n getOptions() {\n return options\n },\n }\n}\n\nfunction mergeCallbacks(allCallbacks: VisitorCallbacks[]): Record<string, (node: any) => void> {\n const callbacksByKey: Record<string, Array<(node: any) => void>> = {}\n\n for (const callbacks of allCallbacks) {\n for (const [key, fn] of Object.entries(callbacks)) {\n const existing = callbacksByKey[key]\n if (existing) {\n existing.push(fn as (node: any) => void)\n } else {\n callbacksByKey[key] = [fn as (node: any) => void]\n }\n }\n }\n\n const merged: Record<string, (node: any) => void> = {}\n for (const [key, fns] of Object.entries(callbacksByKey)) {\n const first = fns[0]\n if (fns.length === 1 && first) {\n merged[key] = first\n } else {\n merged[key] = (node: any) => {\n for (const fn of fns) fn(node)\n }\n }\n }\n return merged\n}\n\n/**\n * Lint a single file and return diagnostics.\n *\n * @example\n * ```ts\n * const result = lintFile(\"app.tsx\", source, allRules, getPreset(\"recommended\"))\n * for (const d of result.diagnostics) console.log(d.message)\n * ```\n */\nexport function lintFile(\n filePath: string,\n sourceText: string,\n rules: Rule[],\n config: LintConfig,\n cache?: AstCache | undefined,\n /**\n * Optional sink for config-level diagnostics (malformed rule options).\n * When provided, diagnostics are appended to it instead of printed to\n * stderr — `lint()` uses this to surface them on `LintResult`.\n */\n configDiagnosticsSink?: ConfigDiagnostic[],\n): LintFileResult {\n const ext = getExtension(filePath)\n if (!JS_EXTENSIONS.has(ext)) {\n return { filePath, diagnostics: [] }\n }\n\n // Try cache first\n let lineIndex: LineIndex\n let program: any\n const cached = cache?.get(sourceText)\n if (cached) {\n lineIndex = cached.lineIndex\n program = cached.program\n } else {\n lineIndex = new LineIndex(sourceText)\n try {\n const result = parseSync(filePath, sourceText, {\n sourceType: 'module',\n lang: getLang(ext),\n })\n program = result.program\n } catch {\n return { filePath, diagnostics: [] }\n }\n cache?.set(sourceText, { program, lineIndex })\n }\n\n const diagnostics: Diagnostic[] = []\n\n // Filter to enabled rules and create visitor callbacks\n const allCallbacks: VisitorCallbacks[] = []\n for (const rule of rules) {\n const entry = config.rules[rule.meta.id]\n if (entry === undefined) continue\n // Normalize bare severity vs `[severity, options]` tuple.\n const [severity, options]: [Severity, RuleOptions] = Array.isArray(entry)\n ? [entry[0] as Severity, (entry[1] ?? {}) as RuleOptions]\n : [entry as Severity, {}]\n if (severity === 'off') continue\n\n // Validate options against the rule's declared schema. Cached per\n // (rule, options) pair — config doesn't change within a run.\n const cacheKey = `${rule.meta.id}::${JSON.stringify(options)}`\n let cached = VALIDATION_CACHE.get(cacheKey)\n if (!cached) {\n const { errors, warnings } = validateRuleOptions(rule, options)\n const configDiags: ConfigDiagnostic[] = []\n for (const message of warnings) {\n configDiags.push({ ruleId: rule.meta.id, severity: 'warn', message })\n }\n for (const message of errors) {\n configDiags.push({ ruleId: rule.meta.id, severity: 'error', message })\n }\n cached = { ok: errors.length === 0, diagnostics: configDiags }\n VALIDATION_CACHE.set(cacheKey, cached)\n }\n // Surface config diagnostics once per (rule, options) pair: prefer\n // the caller-supplied sink (so `lint()` can put them on LintResult);\n // fall back to stderr for standalone `lintFile` usage.\n if (cached.diagnostics.length > 0) {\n if (configDiagnosticsSink) {\n // Dedupe within the sink by (ruleId, message) so two different rules\n // that happen to produce an identical message don't collapse.\n for (const d of cached.diagnostics) {\n if (\n !configDiagnosticsSink.some(\n (x) => x.ruleId === d.ruleId && x.message === d.message,\n )\n ) {\n configDiagnosticsSink.push(d)\n }\n }\n } else {\n for (const d of cached.diagnostics) {\n // oxlint-disable-next-line no-console\n const emit = d.severity === 'error' ? console.error : console.warn\n emit(`[pyreon-lint] ${d.message}`)\n }\n }\n }\n // Hard error in options → skip this rule entirely for the run.\n if (!cached.ok) continue\n\n const ctx = createRuleContext(\n rule,\n severity,\n options,\n diagnostics,\n lineIndex,\n sourceText,\n filePath,\n )\n allCallbacks.push(rule.create(ctx))\n }\n\n // Walk the AST\n const visitor = new Visitor(mergeCallbacks(allCallbacks))\n visitor.visit(program)\n\n // Filter suppressed diagnostics. Two equivalent comment syntaxes:\n // // pyreon-lint-ignore — suppress all on next line\n // // pyreon-lint-ignore <rule-id> — suppress one rule\n // // pyreon-lint-disable-next-line — alias of `ignore`\n // // pyreon-lint-disable-next-line <rule-id> — alias of `ignore <rule-id>`\n // The `disable-next-line` form is the convention several rule docstrings\n // already document — we accept both so the docs and runtime match.\n // Word-boundary matching prevents typos like `// pyreon-lint-ignored` from\n // accidentally being treated as suppressions.\n const lines = sourceText.split('\\n')\n const SUPPRESS_RE = /^\\/\\/\\s*pyreon-lint-(?:ignore|disable-next-line)(?:\\s+(\\S+))?\\s*$/\n const filtered = diagnostics.filter((d) => {\n const prevLineIdx = d.loc.line - 2\n if (prevLineIdx < 0) return true\n const prevLine = lines[prevLineIdx]?.trim() ?? ''\n const match = SUPPRESS_RE.exec(prevLine)\n if (!match) return true\n const ruleId = match[1]\n // Bare suppression (no rule id) → suppress every diagnostic on next line.\n if (!ruleId) return false\n // Rule-specific suppression → drop only the matching rule.\n return ruleId !== d.ruleId\n })\n\n filtered.sort((a, b) => a.span.start - b.span.start)\n return { filePath, diagnostics: filtered }\n}\n\n/**\n * Apply all auto-fixes to a source text.\n * Fixes are applied in reverse order to maintain correct offsets.\n */\nexport function applyFixes(sourceText: string, diagnostics: Diagnostic[]): string {\n const fixable = diagnostics.filter((d) => d.fix !== undefined)\n if (fixable.length === 0) return sourceText\n\n // Sort by start position descending (apply from end to start)\n const sorted = [...fixable].sort((a, b) => {\n const aFix = a.fix\n const bFix = b.fix\n if (!aFix || !bFix) return 0\n return bFix.span.start - aFix.span.start\n })\n\n let result = sourceText\n for (const diag of sorted) {\n const fix = diag.fix\n if (!fix) continue\n result = result.slice(0, fix.span.start) + fix.replacement + result.slice(fix.span.end)\n }\n\n return result\n}\n","import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { AstCache } from './cache'\nimport { createIgnoreFilter } from './config/ignore'\nimport { loadConfig, loadConfigFromPath } from './config/loader'\nimport { getPreset } from './config/presets'\nimport { allRules } from './rules/index'\nimport { applyFixes, lintFile } from './runner'\nimport type {\n ConfigDiagnostic,\n LintConfig,\n LintFileResult,\n LintOptions,\n LintResult,\n RuleMeta,\n RuleOptions,\n Severity,\n} from './types'\nimport { hasJsExtension } from './utils/index'\n\nfunction isHiddenOrVendor(entry: string): boolean {\n return entry.startsWith('.') || entry === 'node_modules' || entry === 'lib' || entry === 'dist'\n}\n\nfunction matchesPatterns(\n filePath: string,\n include?: string[] | undefined,\n exclude?: string[] | undefined,\n): boolean {\n if (exclude) {\n for (const pattern of exclude) {\n if (filePath.includes(pattern)) return false\n }\n }\n if (include && include.length > 0) {\n for (const pattern of include) {\n if (filePath.includes(pattern)) return true\n }\n return false\n }\n return true\n}\n\nfunction walkDirectory(\n dir: string,\n files: string[],\n isIgnored: (filePath: string) => boolean,\n include?: string[] | undefined,\n exclude?: string[] | undefined,\n): void {\n let entries: string[]\n try {\n entries = readdirSync(dir)\n } catch {\n return\n }\n for (const entry of entries) {\n if (isHiddenOrVendor(entry)) continue\n const full = join(dir, entry)\n if (isIgnored(full)) continue\n processEntry(full, files, isIgnored, include, exclude)\n }\n}\n\nfunction processEntry(\n full: string,\n files: string[],\n isIgnored: (filePath: string) => boolean,\n include?: string[] | undefined,\n exclude?: string[] | undefined,\n): void {\n let stat: ReturnType<typeof statSync>\n try {\n stat = statSync(full)\n } catch {\n return\n }\n if (stat.isDirectory()) {\n walkDirectory(full, files, isIgnored, include, exclude)\n } else if (stat.isFile() && hasJsExtension(full) && matchesPatterns(full, include, exclude)) {\n files.push(full)\n }\n}\n\nfunction collectFiles(\n dir: string,\n isIgnored: (filePath: string) => boolean,\n include?: string[] | undefined,\n exclude?: string[] | undefined,\n): string[] {\n const files: string[] = []\n walkDirectory(dir, files, isIgnored, include, exclude)\n return files\n}\n\nfunction buildConfig(options: LintOptions): {\n config: LintConfig\n include: string[] | undefined\n exclude: string[] | undefined\n isIgnored: (filePath: string) => boolean\n} {\n const cwd = resolve('.')\n const fileConfig = options.config ? loadConfigFromPath(options.config) : loadConfig(cwd)\n\n const presetName = options.preset ?? fileConfig?.preset ?? 'recommended'\n const config = getPreset(presetName)\n\n // Merge config file rule overrides. Entries can be a bare severity or a\n // `[severity, options]` tuple — passed through verbatim; the runner\n // normalizes at use-site.\n if (fileConfig?.rules) {\n for (const [id, entry] of Object.entries(fileConfig.rules)) {\n config.rules[id] = entry\n }\n }\n\n // CLI rule severity overrides (highest priority for severity).\n if (options.ruleOverrides) {\n for (const [id, severity] of Object.entries(options.ruleOverrides)) {\n config.rules[id] = severity\n }\n }\n\n // CLI rule options overrides (`--rule-options id='{json}'`). Merged\n // on top of any options already configured for the rule. If the rule\n // is currently bare-severity, we promote it to tuple form using its\n // existing severity (or `recommended` default if not present).\n if (options.ruleOptionsOverrides) {\n for (const [id, optionOverrides] of Object.entries(options.ruleOptionsOverrides)) {\n const existing = config.rules[id]\n const [currentSeverity, currentOptions]: [Severity, RuleOptions] = Array.isArray(existing)\n ? [existing[0] as Severity, (existing[1] ?? {}) as RuleOptions]\n : [(existing ?? 'off') as Severity, {}]\n if (currentSeverity === 'off') continue\n config.rules[id] = [\n currentSeverity,\n { ...currentOptions, ...optionOverrides },\n ] as const\n }\n }\n\n return {\n config,\n include: fileConfig?.include,\n exclude: fileConfig?.exclude,\n isIgnored: createIgnoreFilter(cwd, options.ignore),\n }\n}\n\nfunction gatherFiles(\n paths: string[],\n isIgnored: (filePath: string) => boolean,\n include?: string[] | undefined,\n exclude?: string[] | undefined,\n): string[] {\n const files: string[] = []\n for (const p of paths) {\n const resolved = resolve(p)\n let stat: ReturnType<typeof statSync>\n try {\n stat = statSync(resolved)\n } catch {\n continue\n }\n if (stat.isDirectory()) {\n files.push(...collectFiles(resolved, isIgnored, include, exclude))\n } else if (stat.isFile() && !isIgnored(resolved)) {\n files.push(resolved)\n }\n }\n return files\n}\n\nfunction applyFixesToFile(fileResult: LintFileResult, source: string): void {\n const fixable = fileResult.diagnostics.filter((d) => d.fix)\n if (fixable.length === 0) return\n const fixed = applyFixes(source, fileResult.diagnostics)\n writeFileSync(fileResult.filePath, fixed, 'utf-8')\n fileResult.fixedSource = fixed\n fileResult.diagnostics = fileResult.diagnostics.filter((d) => !d.fix)\n}\n\nfunction countDiagnostics(fileResult: LintFileResult, results: LintResult): void {\n for (const d of fileResult.diagnostics) {\n if (d.severity === 'error') results.totalErrors++\n else if (d.severity === 'warn') results.totalWarnings++\n else if (d.severity === 'info') results.totalInfos++\n }\n}\n\n/**\n * Lint files and return results.\n *\n * @example\n * ```ts\n * import { lint } from \"@pyreon/lint\"\n *\n * const result = lint({ paths: [\"src/\"], preset: \"recommended\" })\n * console.log(result.totalErrors) // 0\n * ```\n */\nexport function lint(options: LintOptions): LintResult {\n const { config, include, exclude, isIgnored } = buildConfig(options)\n const cache = new AstCache()\n const files = gatherFiles(options.paths, isIgnored, include, exclude)\n\n const configDiagnostics: ConfigDiagnostic[] = []\n const results: LintResult = {\n files: [],\n totalErrors: 0,\n totalWarnings: 0,\n totalInfos: 0,\n configDiagnostics,\n }\n\n for (const filePath of files) {\n let source: string\n try {\n source = readFileSync(filePath, 'utf-8')\n } catch {\n continue\n }\n const fileResult = lintFile(filePath, source, allRules, config, cache, configDiagnostics)\n if (options.fix) {\n applyFixesToFile(fileResult, source)\n }\n if (options.quiet) {\n fileResult.diagnostics = fileResult.diagnostics.filter((d) => d.severity === 'error')\n }\n countDiagnostics(fileResult, results)\n results.files.push(fileResult)\n }\n\n return results\n}\n\n/**\n * List all available rules with their metadata.\n *\n * @example\n * ```ts\n * import { listRules } from \"@pyreon/lint\"\n *\n * for (const rule of listRules()) {\n * console.log(`${rule.id} (${rule.severity}): ${rule.description}`)\n * }\n * ```\n */\nexport function listRules(): RuleMeta[] {\n return allRules.map((r) => r.meta)\n}\n","import type { LintResult, Severity } from './types'\n\n// ANSI colors\nconst BOLD = '\\x1b[1m'\nconst RED = '\\x1b[31m'\nconst YELLOW = '\\x1b[33m'\nconst BLUE = '\\x1b[34m'\nconst DIM = '\\x1b[2m'\nconst RESET = '\\x1b[0m'\n\nconst SEVERITY_SYMBOL: Record<Severity, string> = {\n error: `${RED}\\u2716${RESET}`,\n warn: `${YELLOW}\\u26A0${RESET}`,\n info: `${BLUE}\\u2139${RESET}`,\n off: '',\n}\n\nconst SEVERITY_LABEL: Record<Severity, string> = {\n error: `${RED}error${RESET}`,\n warn: `${YELLOW}warning${RESET}`,\n info: `${BLUE}info${RESET}`,\n off: '',\n}\n\n/**\n * Format results as human-readable colored text.\n */\nexport function formatText(result: LintResult): string {\n const lines: string[] = []\n\n for (const file of result.files) {\n if (file.diagnostics.length === 0) continue\n\n lines.push('')\n lines.push(`${BOLD}${file.filePath}${RESET}`)\n\n for (const d of file.diagnostics) {\n const loc = `${DIM}${d.loc.line}:${d.loc.column}${RESET}`\n const severity = SEVERITY_LABEL[d.severity]\n const ruleId = `${DIM}${d.ruleId}${RESET}`\n lines.push(` ${loc} ${severity} ${d.message} ${ruleId}`)\n }\n }\n\n const total = result.totalErrors + result.totalWarnings + result.totalInfos\n if (total > 0) {\n lines.push('')\n const parts: string[] = []\n if (result.totalErrors > 0)\n parts.push(`${RED}${result.totalErrors} error${result.totalErrors === 1 ? '' : 's'}${RESET}`)\n if (result.totalWarnings > 0)\n parts.push(\n `${YELLOW}${result.totalWarnings} warning${result.totalWarnings === 1 ? '' : 's'}${RESET}`,\n )\n if (result.totalInfos > 0) parts.push(`${BLUE}${result.totalInfos} info${RESET}`)\n lines.push(`${SEVERITY_SYMBOL.error} ${parts.join(', ')}`)\n lines.push('')\n }\n\n return lines.join('\\n')\n}\n\n/**\n * Format results as JSON.\n */\nexport function formatJSON(result: LintResult): string {\n return JSON.stringify(result, null, 2)\n}\n\n/**\n * Format results as compact single-line-per-diagnostic output.\n */\nexport function formatCompact(result: LintResult): string {\n const lines: string[] = []\n\n for (const file of result.files) {\n for (const d of file.diagnostics) {\n lines.push(\n `${file.filePath}:${d.loc.line}:${d.loc.column}: ${d.severity} [${d.ruleId}] ${d.message}`,\n )\n }\n }\n\n return lines.join('\\n')\n}\n","/**\n * Minimal LSP server for @pyreon/lint.\n *\n * Provides real-time Pyreon-specific diagnostics in editors that support\n * the Language Server Protocol (VS Code, Neovim, etc.).\n *\n * Usage: pyreon-lint --lsp\n *\n * The server communicates via JSON-RPC over stdin/stdout following the\n * LSP specification (https://microsoft.github.io/language-server-protocol/).\n *\n * Supported capabilities:\n * - textDocument/didOpen — lint on open\n * - textDocument/didSave — lint on save\n * - textDocument/didChange — lint on change (debounced)\n *\n * @module\n */\n\nimport { AstCache } from '../cache'\nimport { getPreset } from '../config/presets'\nimport { allRules } from '../rules/index'\nimport { _resetConfigDiagnosticsCache, lintFile } from '../runner'\nimport type { Diagnostic, LintConfig } from '../types'\n\nconst cache = new AstCache()\nlet config: LintConfig = getPreset('recommended')\n\n/**\n * Reload the LSP's lint config. Resets the runner's per-process\n * validation cache so newly-configured options are re-validated against\n * rule schemas on the next lint pass — needed when the user edits\n * `.pyreonlintrc.json` mid-session. Future hookup point for an LSP\n * `workspace/didChangeConfiguration` notification.\n */\nexport function _reloadConfig(next: LintConfig): void {\n config = next\n _resetConfigDiagnosticsCache()\n cache.clear()\n}\n\n// ─── JSON-RPC message types ────────────────────────────────────────────────\n\ninterface JsonRpcMessage {\n jsonrpc: '2.0'\n id?: number | string | undefined\n method?: string | undefined\n params?: any\n result?: any\n}\n\n// ─── LSP Diagnostic conversion ─────────────────────────────────────────────\n\ninterface LspDiagnostic {\n range: { start: { line: number; character: number }; end: { line: number; character: number } }\n severity: number\n source: string\n message: string\n code: string\n}\n\nfunction toLspDiagnostics(diagnostics: Diagnostic[]): LspDiagnostic[] {\n return diagnostics.map((d) => ({\n range: {\n start: { line: d.loc.line - 1, character: d.loc.column - 1 },\n end: { line: d.loc.line - 1, character: d.loc.column - 1 + (d.span.end - d.span.start) },\n },\n severity: d.severity === 'error' ? 1 : d.severity === 'warn' ? 2 : 3,\n source: 'pyreon-lint',\n message: d.message,\n code: d.ruleId,\n }))\n}\n\n// ─── Lint a document ───────────────────────────────────────────────────────\n\nfunction lintDocument(uri: string, text: string): LspDiagnostic[] {\n try {\n const filePath = uri.replace('file://', '')\n const result = lintFile(filePath, text, allRules, config, cache)\n return toLspDiagnostics(result.diagnostics)\n } catch {\n // Parse errors, unsupported file types — return empty diagnostics\n return []\n }\n}\n\n// ─── Debounce ──────────────────────────────────────────────────────────────\n\nconst DEBOUNCE_MS = 150\nconst debounceTimers = new Map<string, ReturnType<typeof setTimeout>>()\n\nfunction debounceLint(uri: string, text: string): void {\n const existing = debounceTimers.get(uri)\n if (existing) clearTimeout(existing)\n debounceTimers.set(\n uri,\n setTimeout(() => {\n debounceTimers.delete(uri)\n const diagnostics = lintDocument(uri, text)\n sendNotification('textDocument/publishDiagnostics', { uri, diagnostics })\n }, DEBOUNCE_MS),\n )\n}\n\n// ─── Message handling ──────────────────────────────────────────────────────\n\nconst openDocuments = new Map<string, string>()\n\nfunction handleMessage(msg: JsonRpcMessage): JsonRpcMessage | null {\n if (msg.method === 'initialize') {\n return {\n jsonrpc: '2.0',\n id: msg.id,\n result: {\n capabilities: {\n textDocumentSync: 1, // Full sync\n diagnosticProvider: { interFileDependencies: false, workspaceDiagnostics: false },\n },\n serverInfo: { name: 'pyreon-lint', version: '0.11.5' },\n },\n }\n }\n\n if (msg.method === 'initialized') {\n return null // no response needed\n }\n\n if (msg.method === 'textDocument/didOpen') {\n const { uri, text } = msg.params.textDocument\n openDocuments.set(uri, text)\n const diagnostics = lintDocument(uri, text)\n sendNotification('textDocument/publishDiagnostics', { uri, diagnostics })\n return null\n }\n\n if (msg.method === 'textDocument/didChange') {\n const uri = msg.params.textDocument.uri\n const text = msg.params.contentChanges[0]?.text\n if (text != null) {\n openDocuments.set(uri, text)\n // Debounce: wait 150ms after last keystroke before linting\n debounceLint(uri, text)\n }\n return null\n }\n\n if (msg.method === 'textDocument/didSave') {\n const uri = msg.params.textDocument.uri\n const text = openDocuments.get(uri)\n if (text) {\n const diagnostics = lintDocument(uri, text)\n sendNotification('textDocument/publishDiagnostics', { uri, diagnostics })\n }\n return null\n }\n\n if (msg.method === 'textDocument/didClose') {\n const uri = msg.params.textDocument.uri\n openDocuments.delete(uri)\n sendNotification('textDocument/publishDiagnostics', { uri, diagnostics: [] })\n return null\n }\n\n if (msg.method === 'shutdown') {\n return { jsonrpc: '2.0', id: msg.id, result: null }\n }\n\n if (msg.method === 'exit') {\n process.exit(0)\n }\n\n // Unknown method\n if (msg.id != null) {\n return {\n jsonrpc: '2.0',\n id: msg.id,\n result: null,\n }\n }\n\n return null\n}\n\n// ─── JSON-RPC transport (stdin/stdout) ─────────────────────────────────────\n\nfunction sendMessage(msg: JsonRpcMessage) {\n const body = JSON.stringify(msg)\n const header = `Content-Length: ${Buffer.byteLength(body)}\\r\\n\\r\\n`\n process.stdout.write(header + body)\n}\n\nfunction sendNotification(method: string, params: any) {\n sendMessage({ jsonrpc: '2.0', method, params })\n}\n\n/**\n * Start the LSP server. Reads JSON-RPC messages from stdin,\n * processes them, and writes responses to stdout.\n */\nexport function startLspServer(): void {\n let buffer = ''\n\n process.stdin.setEncoding('utf-8')\n process.stdin.on('data', (chunk: string) => {\n buffer += chunk\n\n // Parse Content-Length header + body\n while (true) {\n const headerEnd = buffer.indexOf('\\r\\n\\r\\n')\n if (headerEnd === -1) break\n\n const header = buffer.slice(0, headerEnd)\n const match = header.match(/Content-Length:\\s*(\\d+)/i)\n if (!match) {\n buffer = buffer.slice(headerEnd + 4)\n continue\n }\n\n const contentLength = Number.parseInt(match[1]!, 10)\n const bodyStart = headerEnd + 4\n if (buffer.length < bodyStart + contentLength) break\n\n const body = buffer.slice(bodyStart, bodyStart + contentLength)\n buffer = buffer.slice(bodyStart + contentLength)\n\n try {\n const msg = JSON.parse(body) as JsonRpcMessage\n const response = handleMessage(msg)\n if (response) sendMessage(response)\n } catch {\n // malformed JSON — ignore\n }\n }\n })\n\n process.stderr.write('[pyreon-lint] LSP server started\\n')\n}\n","import { readFileSync, watch } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { AstCache } from './cache'\nimport { createIgnoreFilter } from './config/ignore'\nimport { getPreset } from './config/presets'\nimport { formatCompact, formatJSON, formatText } from './reporter'\nimport { allRules } from './rules/index'\nimport { lintFile } from './runner'\nimport type { LintConfig, LintOptions, LintResult, Severity } from './types'\nimport { hasJsExtension } from './utils/index'\n\nfunction formatOutput(result: LintResult, format: string): string {\n if (format === 'json') return formatJSON(result)\n if (format === 'compact') return formatCompact(result)\n return formatText(result)\n}\n\n/**\n * Watch directories and re-lint changed files.\n *\n * Uses `fs.watch` (recursive) with 100ms debounce.\n * Caches ASTs for unchanged files.\n *\n * @example\n * ```ts\n * import { watchAndLint } from \"@pyreon/lint\"\n *\n * watchAndLint({ paths: [\"src/\"], preset: \"recommended\", format: \"text\" })\n * ```\n */\nexport function watchAndLint(options: LintOptions & { format: string }): void {\n const cache = new AstCache()\n const preset = options.preset ?? 'recommended'\n const config = getPreset(preset)\n\n applyOverrides(config, options.ruleOverrides)\n applyOptionsOverrides(config, options.ruleOptionsOverrides)\n\n const cwd = resolve('.')\n const isIgnored = createIgnoreFilter(cwd, options.ignore)\n\n // Debounce map: filePath -> timeout\n const pending = new Map<string, ReturnType<typeof setTimeout>>()\n\n // oxlint-disable-next-line no-console\n console.log(`\\x1b[2m[pyreon-lint] Watching for changes...\\x1b[0m\\n`)\n\n for (const p of options.paths) {\n const dir = resolve(p)\n try {\n watch(dir, { recursive: true }, (_event, filename) => {\n if (!filename) return\n const filePath = resolve(dir, filename)\n\n if (!hasJsExtension(filePath) || isIgnored(filePath)) return\n\n // Debounce: clear existing timeout for this file\n const existing = pending.get(filePath)\n if (existing) clearTimeout(existing)\n\n pending.set(\n filePath,\n setTimeout(() => {\n pending.delete(filePath)\n relintFile(filePath, config, cache, options.format)\n }, 100),\n )\n })\n } catch {\n console.error(`[pyreon-lint] Could not watch: ${dir}`)\n }\n }\n}\n\nfunction applyOverrides(\n config: LintConfig,\n overrides?: Record<string, Severity> | undefined,\n): void {\n if (!overrides) return\n for (const [id, severity] of Object.entries(overrides)) {\n config.rules[id] = severity\n }\n}\n\nfunction applyOptionsOverrides(\n config: LintConfig,\n overrides?: Record<string, Record<string, unknown>> | undefined,\n): void {\n if (!overrides) return\n for (const [id, opts] of Object.entries(overrides)) {\n const existing = config.rules[id]\n const [severity, current]: [Severity, Record<string, unknown>] = Array.isArray(existing)\n ? [existing[0] as Severity, (existing[1] ?? {}) as Record<string, unknown>]\n : [(existing ?? 'off') as Severity, {}]\n if (severity === 'off') continue\n config.rules[id] = [severity, { ...current, ...opts }] as const\n }\n}\n\nfunction relintFile(filePath: string, config: LintConfig, cache: AstCache, format: string): void {\n let source: string\n try {\n source = readFileSync(filePath, 'utf-8')\n } catch {\n return\n }\n\n const fileResult = lintFile(filePath, source, allRules, config, cache)\n\n if (fileResult.diagnostics.length === 0) return\n\n const result: LintResult = {\n files: [fileResult],\n totalErrors: 0,\n totalWarnings: 0,\n totalInfos: 0,\n configDiagnostics: [],\n }\n\n for (const d of fileResult.diagnostics) {\n if (d.severity === 'error') result.totalErrors++\n else if (d.severity === 'warn') result.totalWarnings++\n else if (d.severity === 'info') result.totalInfos++\n }\n\n // Clear screen and print\n process.stdout.write('\\x1b[2J\\x1b[H')\n console.log(formatOutput(result, format))\n}\n"],"mappings":"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"}