@fairfox/polly 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/polly.js +21 -1
- package/dist/cli/polly.js.map +3 -3
- package/dist/src/actions/error.d.ts +26 -0
- package/dist/src/actions/event-delegation.d.ts +48 -0
- package/dist/src/actions/form.d.ts +72 -0
- package/dist/src/actions/index.d.ts +13 -0
- package/dist/src/actions/index.js +525 -0
- package/dist/src/actions/index.js.map +15 -0
- package/dist/src/actions/overlay.d.ts +26 -0
- package/dist/src/actions/registry.d.ts +25 -0
- package/dist/src/actions/store.d.ts +26 -0
- package/dist/src/actions/testing.d.ts +26 -0
- package/dist/src/background/index.js +26 -1
- package/dist/src/background/index.js.map +2 -2
- package/dist/src/background/message-router.js +26 -1
- package/dist/src/background/message-router.js.map +2 -2
- package/dist/src/client/index.js +27 -2
- package/dist/src/client/index.js.map +3 -3
- package/dist/src/elysia/index.js +26 -1
- package/dist/src/elysia/index.js.map +2 -2
- package/dist/src/index.js +26 -1
- package/dist/src/index.js.map +2 -2
- package/dist/src/mesh-node.js +26 -1
- package/dist/src/mesh-node.js.map +2 -2
- package/dist/src/mesh.js +26 -1
- package/dist/src/mesh.js.map +2 -2
- package/dist/src/peer.js +26 -1
- package/dist/src/peer.js.map +2 -2
- package/dist/src/polly-ui/ActionForm.d.ts +21 -0
- package/dist/src/polly-ui/ActionInput.d.ts +41 -0
- package/dist/src/polly-ui/ConfirmDialog.d.ts +24 -0
- package/dist/src/polly-ui/Layout.d.ts +51 -0
- package/dist/src/polly-ui/Modal.d.ts +52 -0
- package/dist/src/polly-ui/OverlayRoot.d.ts +10 -0
- package/dist/src/polly-ui/TextInput.d.ts +31 -0
- package/dist/src/polly-ui/Toast.d.ts +19 -0
- package/dist/src/polly-ui/index.css +319 -0
- package/dist/src/polly-ui/index.d.ts +17 -0
- package/dist/src/polly-ui/index.js +953 -0
- package/dist/src/polly-ui/index.js.map +22 -0
- package/dist/src/polly-ui/internal/focus-trap.d.ts +10 -0
- package/dist/src/polly-ui/internal/input-base.d.ts +18 -0
- package/dist/src/polly-ui/internal/scroll-lock.d.ts +9 -0
- package/dist/src/polly-ui/styles.css +70 -0
- package/dist/src/polly-ui/theme.css +163 -0
- package/dist/src/shared/adapters/index.js +26 -1
- package/dist/src/shared/adapters/index.js.map +2 -2
- package/dist/src/shared/lib/context-helpers.js +26 -1
- package/dist/src/shared/lib/context-helpers.js.map +2 -2
- package/dist/src/shared/lib/errors.js +26 -1
- package/dist/src/shared/lib/errors.js.map +2 -2
- package/dist/src/shared/lib/message-bus.js +26 -1
- package/dist/src/shared/lib/message-bus.js.map +2 -2
- package/dist/src/shared/lib/resource.js +26 -1
- package/dist/src/shared/lib/resource.js.map +2 -2
- package/dist/src/shared/lib/state.js +26 -1
- package/dist/src/shared/lib/state.js.map +2 -2
- package/dist/src/shared/lib/test-helpers.js +26 -1
- package/dist/src/shared/lib/test-helpers.js.map +2 -2
- package/dist/src/shared/state/app-state.js +26 -1
- package/dist/src/shared/state/app-state.js.map +2 -2
- package/dist/src/shared/types/messages.js +26 -1
- package/dist/src/shared/types/messages.js.map +2 -2
- package/dist/tools/quality/src/cli.js +640 -28
- package/dist/tools/quality/src/cli.js.map +11 -5
- package/dist/tools/quality/src/css/check-layout.d.ts +19 -0
- package/dist/tools/quality/src/css/check-quality.d.ts +24 -0
- package/dist/tools/quality/src/css/check-unused.d.ts +20 -0
- package/dist/tools/quality/src/css/check-vars.d.ts +22 -0
- package/dist/tools/quality/src/css/shared.d.ts +33 -0
- package/dist/tools/quality/src/index.d.ts +12 -0
- package/dist/tools/quality/src/index.js +557 -18
- package/dist/tools/quality/src/index.js.map +10 -4
- package/dist/tools/quality/src/logger.d.ts +26 -0
- package/dist/tools/test/src/adapters/index.js +26 -1
- package/dist/tools/test/src/adapters/index.js.map +2 -2
- package/dist/tools/test/src/browser/index.js +26 -1
- package/dist/tools/test/src/browser/index.js.map +2 -2
- package/dist/tools/test/src/browser/run.js +238 -0
- package/dist/tools/test/src/browser/run.js.map +11 -0
- package/dist/tools/test/src/index.js +26 -1
- package/dist/tools/test/src/index.js.map +2 -2
- package/dist/tools/test/src/test-utils.js +26 -1
- package/dist/tools/test/src/test-utils.js.map +2 -2
- package/dist/tools/test/src/visual/compare.d.ts +23 -0
- package/dist/tools/test/src/visual/harness.d.ts +53 -0
- package/dist/tools/test/src/visual/index.d.ts +12 -0
- package/dist/tools/test/src/visual/index.js +13968 -0
- package/dist/tools/test/src/visual/index.js.map +41 -0
- package/dist/tools/verify/src/cli.js +3 -3
- package/dist/tools/verify/src/cli.js.map +1 -1
- package/dist/tools/verify/src/config.js +26 -1
- package/dist/tools/verify/src/config.js.map +2 -2
- package/package.json +21 -2
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../tools/quality/src/no-as-casting.ts", "../tools/quality/src/cli.ts"],
|
|
3
|
+
"sources": ["../tools/quality/src/css/shared.ts", "../tools/quality/src/logger.ts", "../tools/quality/src/css/check-layout.ts", "../tools/quality/src/css/check-quality.ts", "../tools/quality/src/css/check-unused.ts", "../tools/quality/src/css/check-vars.ts", "../tools/quality/src/no-as-casting.ts", "../tools/quality/src/cli.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * No-as-casting conformance check.\n *\n * Bans all TypeScript type assertions (`as Type`) except the allowed\n * patterns: `as const` (literal narrowing), `as unknown as` (explicit\n * escape hatch), import/export renames, and `as` inside strings or\n * comments. Violations include pattern-specific fix advice.\n *\n * This module exports the check logic as a library so consuming\n * applications can import it from `@fairfox/polly/quality` and run it\n * programmatically. Polly's own `scripts/check-no-as-casting.ts` is a\n * thin CLI wrapper around these exports.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { Glob } from \"bun\";\n\nexport interface Violation {\n file: string;\n line: number;\n content: string;\n advice?: string;\n}\n\nexport interface CheckResult {\n violations: Violation[];\n print: () => void;\n}\n\nexport interface CheckOptions {\n rootDir: string;\n exclude?: string[];\n excludePackages?: string[];\n excludeFiles?: string[];\n filePatterns?: string;\n}\n\n/**\n * Check whether a line contains a forbidden `as` type assertion.\n * Returns true if the line is clean (no violation), false if it violates.\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Source-text scanning with many skip rules is inherently branchy.\nexport function isLineClean(line: string): boolean {\n if (!line.includes(\" as \")) return true;\n\n const trimmed = line.trim();\n\n // Full-line comments\n if (trimmed.startsWith(\"//\") || trimmed.startsWith(\"*\") || trimmed.startsWith(\"/*\")) {\n return true;\n }\n\n // as const (literal narrowing)\n if (line.match(/\\bas const\\b/)) {\n const withoutAsConst = line.replace(/\\bas const\\b/g, \"\");\n if (!withoutAsConst.includes(\" as \")) return true;\n }\n\n // as unknown as (explicit escape hatch)\n if (line.includes(\" as unknown as \") || line.trimEnd().endsWith(\"as unknown as\")) {\n const withoutEscapeHatch = line.replace(/\\bas unknown as\\b/g, \"\");\n if (!withoutEscapeHatch.includes(\" as \")) return true;\n }\n\n // Import/export renames\n if (\n line.match(/\\b(import|export)\\s+.*\\s+as\\s+\\w+/) ||\n line.match(/\\b(import|export)\\s+\\*\\s+as\\s+\\w+/) ||\n line.match(/\\b(import|export)\\s+type\\s+.*\\s+as\\s+\\w+/) ||\n line.match(/^\\s*\\w+\\s+as\\s+\\w+,?\\s*$/) ||\n line.match(/^\\s*type\\s+\\w+\\s+as\\s+\\w+,?\\s*$/)\n ) {\n return true;\n }\n\n // Property declarations: as= or as: or as,\n if (line.match(/\\bas\\s*[=:,]/)) return true;\n\n // String literal detection: count quotes before each ` as ` occurrence.\n // If any quote type has an odd count, the ` as ` is inside a string.\n if (everyAsInsideString(line)) return true;\n\n // JSX text: ` as ` between > and < with no code syntax around it\n if (isJsxText(trimmed)) return true;\n\n // Plain text heuristic: indented line with no code syntax characters\n // before ` as ` — catches multiline JSX text and template literal bodies.\n if (isPlainText(trimmed)) return true;\n\n // Inline comment: ` as ` appears only after //\n const commentIdx = line.indexOf(\"//\");\n if (commentIdx >= 0 && line.indexOf(\" as \", commentIdx) >= 0) {\n const beforeComment = line.substring(0, commentIdx);\n if (!beforeComment.includes(\" as \")) return true;\n }\n\n // SQL alias: `) as column_name`\n if (line.match(/\"\\)\\s+as\\s+\\w+\"/)) return true;\n\n if (line.includes(\" satisfies \")) return true;\n\n return false;\n}\n\n/**\n * Returns true when every ` as ` occurrence in the line falls inside a\n * string literal (single-quoted, double-quoted, or backtick).\n */\nfunction everyAsInsideString(line: string): boolean {\n let searchFrom = 0;\n while (true) {\n const idx = line.indexOf(\" as \", searchFrom);\n if (idx < 0) return true; // no more ` as ` to check\n const before = line.substring(0, idx);\n const singleQuotes = (before.match(/'/g) ?? []).length;\n const doubleQuotes = (before.match(/\"/g) ?? []).length;\n const backticks = (before.match(/`/g) ?? []).length;\n if (singleQuotes % 2 === 0 && doubleQuotes % 2 === 0 && backticks % 2 === 0) {\n return false; // this ` as ` is outside any string\n }\n searchFrom = idx + 4;\n }\n}\n\n/**\n * Detects JSX text content — ` as ` appearing between > and < with no\n * code-like syntax around it (no braces, semicolons, equals signs).\n */\nfunction isJsxText(trimmed: string): boolean {\n // Classic JSX text: starts after > or is plain text ending before <\n if (trimmed.match(/^[^{};=()]*\\bas\\b[^{};=()]*$/)) {\n // No code syntax at all — could be JSX text or template literal body.\n // Reject if it looks like a type assertion (word ` as ` TypeName pattern\n // where TypeName starts with uppercase, or is a known TS type).\n if (\n !trimmed.match(/\\bas\\s+[A-Z]\\w*/) &&\n !trimmed.match(/\\bas\\s+(string|number|boolean|any|unknown|never)\\b/)\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Heuristic for plain text in template literals or JSX: the line has no\n * code-like characters before ` as ` — no `=`, `{`, `}`, `:`, `;`, `(`.\n */\nfunction isPlainText(trimmed: string): boolean {\n const idx = trimmed.indexOf(\" as \");\n if (idx < 0) return false;\n const before = trimmed.substring(0, idx);\n // If nothing before ` as ` looks like code, it's probably prose.\n return (\n !before.match(/[={}:;(]/) &&\n !before.match(/\\b(const|let|var|type|interface|function|return|await)\\b/)\n );\n}\n\n/**\n * Suggest a concrete fix for a specific violation pattern.\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Pattern-matching advice is a linear chain of if-returns.\nexport function suggestFix(line: string): string | undefined {\n if (line.includes(\"JSON.parse\")) {\n return \"Use a validation function or type guard to parse and validate the result.\";\n }\n if (\n line.includes(\"as HTMLInputElement\") ||\n line.includes(\"as HTMLTextAreaElement\") ||\n line.includes(\"as HTMLButtonElement\")\n ) {\n return \"Use instanceof: if (el instanceof HTMLInputElement) { el.value ... }\";\n }\n if (line.includes(\"as HTMLElement\") || line.includes(\"as Element\")) {\n return \"Use instanceof: if (el instanceof HTMLElement) { ... }\";\n }\n if (line.includes(\".doc()\") && line.includes(\"as \")) {\n return \"Type the DocHandle generic: repo.find<MyType>(id) returns DocHandle<MyType>.\";\n }\n if (\n line.includes(\"Record<string, unknown>\") &&\n (line.includes(\"window\") || line.includes(\"globalThis\"))\n ) {\n return \"Extract a type guard: function getGlobalProp(name: string): unknown { ... }\";\n }\n if (line.includes(\"Record<string, unknown>\")) {\n return \"Use a type guard function that narrows the unknown value to the target shape.\";\n }\n if (line.includes(\"as PeerId\") || line.includes(\"as DocumentId\")) {\n return \"Use the library's branded-type constructor if available, or centralise the cast in a factory.\";\n }\n if (line.includes(\"as string\") || line.includes(\"as number\") || line.includes(\"as boolean\")) {\n return \"Narrow with typeof: if (typeof x === 'string') { ... }\";\n }\n if (line.includes(\"as any\")) {\n return \"Replace 'any' with 'unknown' and add a type guard or validation at the boundary.\";\n }\n return undefined;\n}\n\nfunction isFileExcluded(\n relative: string,\n excludeDirs: Set<string>,\n excludePackages: Set<string>,\n excludeFiles: Set<string>\n): boolean {\n const segments = relative.split(\"/\");\n if (segments.some((s) => excludeDirs.has(s))) return true;\n if (excludePackages.size > 0 && segments.some((s) => excludePackages.has(s))) return true;\n const basename = segments[segments.length - 1] ?? \"\";\n return excludeFiles.has(basename) || excludeFiles.has(relative);\n}\n\nfunction findViolations(relative: string, content: string): Violation[] {\n const results: Violation[] = [];\n const lines = content.split(\"\\n\");\n let insideTemplate = false;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? \"\";\n const backticks = (line.match(/`/g) ?? []).length;\n const startedInTemplate = insideTemplate;\n if (backticks % 2 === 1) insideTemplate = !insideTemplate;\n\n // Line is entirely inside a multi-line template literal and has no\n // interpolation — treat as string content (e.g. SQL column aliases).\n if (startedInTemplate && backticks === 0 && !line.includes(\"${\")) continue;\n\n if (!isLineClean(line)) {\n results.push({\n file: relative,\n line: i + 1,\n content: line.trim(),\n advice: suggestFix(line.trim()),\n });\n }\n }\n return results;\n}\n\nfunction printViolations(violations: Violation[]): void {\n if (violations.length === 0) {\n console.log(\"[no-as-casting] ✅ No violations found.\");\n return;\n }\n console.log(`[no-as-casting] ❌ ${violations.length} violation(s) found:\\n`);\n for (const v of violations) {\n console.log(` ${v.file}:${v.line}`);\n console.log(` ${v.content}`);\n if (v.advice) console.log(` 💡 ${v.advice}`);\n console.log();\n }\n console.log(\"[no-as-casting] Use type guards, validation, or fix the types at the source.\");\n console.log('[no-as-casting] Only \"as const\" and \"as unknown as\" are allowed.');\n}\n\n/**\n * Run the no-as-casting check against a directory. Returns a result\n * object with the violations and a print function for CLI output.\n */\nexport async function checkNoAsCasting(options: CheckOptions): Promise<CheckResult> {\n const rootDir = options.rootDir;\n const excludeDirs = new Set(options.exclude ?? [\"node_modules\", \"dist\", \".git\", \".bun\"]);\n const excludePackages = new Set(options.excludePackages ?? []);\n const excludeFiles = new Set(options.excludeFiles ?? []);\n const pattern = options.filePatterns ?? \"**/*.{ts,tsx}\";\n const glob = new Glob(pattern);\n const violations: Violation[] = [];\n\n for await (const file of glob.scan({ cwd: rootDir, absolute: true })) {\n const relative = file.replace(`${rootDir}/`, \"\");\n if (isFileExcluded(relative, excludeDirs, excludePackages, excludeFiles)) continue;\n const content = readFileSync(file, \"utf-8\");\n violations.push(...findViolations(relative, content));\n }\n\n return { violations, print: () => printViolations(violations) };\n}\n",
|
|
6
|
-
"
|
|
5
|
+
"/**\n * Shared plumbing for CSS conformance checks.\n *\n * File discovery, violation printing, and common scanner options live\n * here so each check (`quality`, `layout`, `vars`, `unused`) can stay\n * focused on its own rule set.\n */\n\nimport type { Dirent } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport { logger } from \"../logger.ts\";\n\nexport type CssViolation = {\n file: string;\n line: number;\n rule: string;\n content: string;\n suggestion: string;\n};\n\nexport type CssCheckResult = {\n violations: CssViolation[];\n print: () => void;\n};\n\nexport type CssScanOptions = {\n rootDir: string;\n /** Directory names (not paths) to skip during recursion. */\n skipDirs?: string[];\n /** File names (not paths) to skip entirely. */\n skipFiles?: string[];\n};\n\nexport const DEFAULT_SKIP_DIRS = [\"node_modules\", \".git\", \"dist\", \"dist-test\", \"build\", \"coverage\"];\n\nexport async function walkDirectory(\n dir: string,\n visit: (filePath: string, relPath: string) => Promise<void>,\n opts: CssScanOptions\n): Promise<void> {\n const skipDirs = new Set(opts.skipDirs ?? DEFAULT_SKIP_DIRS);\n const skipFiles = new Set(opts.skipFiles ?? []);\n const rootDir = opts.rootDir;\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: recursive walk with skip sets is inherently branchy.\n async function walk(current: string): Promise<void> {\n let entries: Dirent[];\n try {\n entries = await readdir(current, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const full = join(current, entry.name);\n if (entry.isDirectory()) {\n if (skipDirs.has(entry.name)) continue;\n await walk(full);\n } else if (entry.isFile()) {\n if (skipFiles.has(entry.name)) continue;\n const rel = relative(rootDir, full);\n await visit(full, rel);\n }\n }\n }\n\n await walk(dir);\n}\n\nexport function formatViolations(\n kind: string,\n violations: CssViolation[],\n rootDir: string\n): string[] {\n const lines: string[] = [];\n if (violations.length === 0) {\n lines.push(`✅ ${kind}: no violations`);\n return lines;\n }\n lines.push(`❌ ${kind}: ${violations.length} violation(s)`);\n const byFile = new Map<string, CssViolation[]>();\n for (const v of violations) {\n const bucket = byFile.get(v.file) ?? [];\n bucket.push(v);\n byFile.set(v.file, bucket);\n }\n for (const [file, fileViolations] of byFile) {\n lines.push(` ${relative(rootDir, file)}`);\n for (const v of fileViolations) {\n lines.push(` L${v.line}: ${v.content}`);\n lines.push(` → ${v.suggestion} [${v.rule}]`);\n }\n }\n return lines;\n}\n\nexport function makeResult(\n kind: string,\n rootDir: string,\n violations: CssViolation[]\n): CssCheckResult {\n return {\n violations,\n print() {\n for (const line of formatViolations(kind, violations, rootDir)) {\n if (line.startsWith(\"❌\")) {\n logger.error(line);\n } else {\n logger.log(line);\n }\n }\n },\n };\n}\n\n/** True when the line is a CSS comment (standalone or continuation). */\nexport function isInsideComment(line: string): boolean {\n const trimmed = line.trim();\n return trimmed.startsWith(\"/*\") || trimmed.startsWith(\"*\") || trimmed.startsWith(\"//\");\n}\n\n/** Heuristic: true when the given line number falls inside an @keyframes block. */\nexport function isInsideKeyframes(lineNum: number, allLines: readonly string[]): boolean {\n for (let i = lineNum - 1; i >= 0; i -= 1) {\n const l = allLines[i]?.trim() ?? \"\";\n if (l.startsWith(\"@keyframes\")) return true;\n if (l === \"}\" && i < lineNum - 1) return false;\n }\n return false;\n}\n",
|
|
6
|
+
"/**\n * Logger for the quality tool.\n *\n * One mutable singleton so every check writes through the same sink.\n * Tests replace the methods to capture output, then call `resetLogger()`\n * to restore defaults. No dependency injection chain through every\n * check signature — the singleton pattern matches how Polly's own\n * CLI output is consumed (stdout/stderr at process edge).\n *\n * import { logger, resetLogger } from \"./logger\";\n * logger.error(\"…\");\n *\n * // in tests\n * const captured: string[] = [];\n * logger.error = (m) => captured.push(m);\n * // ... run check ...\n * resetLogger();\n */\n\nexport type QualityLogger = {\n log: (message: string) => void;\n error: (message: string) => void;\n info: (message: string) => void;\n warn: (message: string) => void;\n};\n\nfunction defaultLog(message: string): void {\n console.log(message);\n}\nfunction defaultError(message: string): void {\n // biome-ignore lint/suspicious/noConsole: CLI output is the whole point of the default sink.\n console.error(message);\n}\nfunction defaultInfo(message: string): void {\n // biome-ignore lint/suspicious/noConsole: CLI output is the whole point of the default sink.\n console.info(message);\n}\nfunction defaultWarn(message: string): void {\n // biome-ignore lint/suspicious/noConsole: CLI output is the whole point of the default sink.\n console.warn(message);\n}\n\nexport const logger: QualityLogger = {\n log: defaultLog,\n error: defaultError,\n info: defaultInfo,\n warn: defaultWarn,\n};\n\nexport function resetLogger(): void {\n logger.log = defaultLog;\n logger.error = defaultError;\n logger.info = defaultInfo;\n logger.warn = defaultWarn;\n}\n",
|
|
7
|
+
"/**\n * CSS layout check.\n *\n * `display: flex` and `display: grid` are forbidden outside the Layout\n * primitive. Apps express layout through the `<Layout>` component so\n * layout decisions are declarative and greppable. Exempted paths default\n * to `Layout.module.css`; consumers can extend the list through\n * `layoutExemptPaths`. Suppress one-off cases with a layout-ignore CSS\n * comment on the violating line or the line above.\n */\n\nimport type { CssCheckResult, CssViolation } from \"./shared.ts\";\nimport { makeResult, walkDirectory } from \"./shared.ts\";\n\nexport type CssLayoutOptions = {\n rootDir: string;\n /** File basenames or path fragments where layout CSS is permitted. */\n layoutExemptPaths?: string[];\n /** Directory names skipped during recursion. */\n skipDirs?: string[];\n};\n\nconst CSS_PATTERNS: Array<{ pattern: RegExp; kind: string }> = [\n { pattern: /display\\s*:\\s*flex/, kind: \"display: flex in CSS\" },\n { pattern: /display\\s*:\\s*grid/, kind: \"display: grid in CSS\" },\n];\n\nconst TSX_PATTERNS: Array<{ pattern: RegExp; kind: string }> = [\n {\n pattern: /display\\s*:\\s*['\"]flex['\"]/,\n kind: \"display: flex in inline style\",\n },\n {\n pattern: /display\\s*:\\s*['\"]grid['\"]/,\n kind: \"display: grid in inline style\",\n },\n];\n\nconst SUPPRESS = \"layout-ignore\";\n\nexport async function checkCssLayout(options: CssLayoutOptions): Promise<CssCheckResult> {\n const rootDir = options.rootDir;\n const exempt = options.layoutExemptPaths ?? [\"Layout.module.css\", \"Layout.tsx\"];\n const violations: CssViolation[] = [];\n\n await walkDirectory(\n rootDir,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: per-file visitor covers filetype, suppression, and pattern loop.\n async (full) => {\n const isCss = full.endsWith(\".module.css\");\n const isTsx = full.endsWith(\".tsx\");\n if (!isCss && !isTsx) return;\n if (exempt.some((fragment) => full.includes(fragment))) return;\n\n const patterns = isCss ? CSS_PATTERNS : TSX_PATTERNS;\n const content = await Bun.file(full).text();\n const lines = content.split(\"\\n\");\n\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (!line) continue;\n const trimmed = line.trim();\n if (trimmed.startsWith(\"//\") || trimmed.startsWith(\"*\") || trimmed.startsWith(\"/*\"))\n continue;\n if (trimmed.includes(SUPPRESS)) continue;\n const prev = i > 0 ? (lines[i - 1]?.trim() ?? \"\") : \"\";\n if (prev.includes(SUPPRESS)) continue;\n\n for (const rule of patterns) {\n if (rule.pattern.test(line)) {\n violations.push({\n file: full,\n line: i + 1,\n rule: rule.kind,\n content: trimmed,\n suggestion: \"Use the <Layout> component instead\",\n });\n break;\n }\n }\n }\n },\n {\n rootDir,\n skipDirs: options.skipDirs,\n }\n );\n\n return makeResult(\"css-layout\", rootDir, violations);\n}\n",
|
|
8
|
+
"/**\n * CSS quality check.\n *\n * Every styled value that has a semantic token must reference the token.\n * Raw hex colours, rgba literals, rem values, numeric font-weights,\n * magic z-indexes, hardcoded border-radius / border-width / box-shadow,\n * inline transitions, and `!important` are all rejected. The rule set\n * is intentionally opinionated; consumers who need an escape hatch may\n * place a polly-ignore-all marker in a CSS comment on the first line\n * of a file to skip checks for that file.\n */\n\nimport type { CssCheckResult, CssViolation } from \"./shared.ts\";\nimport { isInsideComment, isInsideKeyframes, makeResult, walkDirectory } from \"./shared.ts\";\n\nexport type CssQualityOptions = {\n rootDir: string;\n /** Scan filter — files whose basename ends with one of these are checked. Default: `.module.css`. */\n extensions?: string[];\n /** File basenames skipped entirely. Default: `[\"theme.css\", \"tokens.css\"]`. */\n skipFiles?: string[];\n /** Directory names skipped during recursion. */\n skipDirs?: string[];\n /** Which rules to disable by id. */\n disableRules?: string[];\n};\n\ntype Rule = {\n id: string;\n check: (line: string, lineNum: number, allLines: readonly string[]) => string | null;\n};\n\nconst DEFAULT_RULES: Rule[] = [\n {\n id: \"no-hardcoded-color\",\n check: (line) => {\n if (isInsideComment(line)) return null;\n if (line.includes(\"var(\")) return null;\n if (/\\bcolor:\\s*(white|black|#[0-9a-fA-F]{3,8})\\b/.test(line)) {\n return \"Use a semantic colour token (--polly-text, --polly-text-muted, …)\";\n }\n if (/background(-color)?:\\s*#[0-9a-fA-F]{3,8}/.test(line) && !line.includes(\"var(\")) {\n return \"Use a semantic surface token (--polly-surface, --polly-surface-raised, …)\";\n }\n return null;\n },\n },\n {\n id: \"no-hardcoded-rgba\",\n check: (line) => {\n if (isInsideComment(line)) return null;\n if (!line.includes(\"rgba(\")) return null;\n if (line.includes(\"var(\")) return null;\n if (line.includes(\"color-mix(\")) return null;\n if (/background.*rgba/.test(line)) {\n return \"Use a semantic surface or overlay token\";\n }\n if (/box-shadow.*rgba/.test(line)) {\n return \"Use a semantic shadow token\";\n }\n return \"Use a semantic token instead of raw rgba()\";\n },\n },\n {\n id: \"no-hardcoded-z-index\",\n check: (line) => {\n const m = line.match(/z-index:\\s*(\\d+)/);\n if (!m?.[1]) return null;\n if (line.includes(\"var(\")) return null;\n if (Number.parseInt(m[1], 10) < 10) return null;\n return \"Use a --polly-z-* token\";\n },\n },\n {\n id: \"no-hardcoded-opacity\",\n check: (line) => {\n if (!/opacity:\\s*0\\.\\d/.test(line)) return null;\n if (line.includes(\"var(\")) return null;\n const m = line.match(/opacity:\\s*(0\\.\\d+)/);\n if (!m?.[1]) return null;\n const v = Number.parseFloat(m[1]);\n if (v >= 0.5 && v <= 0.7) {\n return \"Use var(--polly-opacity-disabled) for disabled states\";\n }\n return null;\n },\n },\n {\n id: \"no-hardcoded-transition\",\n check: (line, lineNum, allLines) => {\n if (isInsideKeyframes(lineNum, allLines)) return null;\n if (line.includes(\"var(--polly-motion\")) return null;\n if (!/(?:transition|animation)/.test(line)) return null;\n if (\n /\\d+(\\.\\d+)?(ms|s)\\s+(ease|linear|ease-in|ease-out|ease-in-out)/.test(line) &&\n !line.includes(\"infinite\")\n ) {\n return \"Use var(--polly-motion-fast|base|slow)\";\n }\n if (/\\s\\d+(\\.\\d+)?s[;\\s,]/.test(line) && !line.includes(\"infinite\")) {\n return \"Use var(--polly-motion-fast|base|slow)\";\n }\n return null;\n },\n },\n {\n id: \"no-important\",\n check: (line) => {\n if (isInsideComment(line)) return null;\n if (!line.includes(\"!important\")) return null;\n return \"Refactor specificity instead of using !important\";\n },\n },\n {\n id: \"no-rem-units\",\n check: (line, lineNum, allLines) => {\n if (isInsideComment(line)) return null;\n if (isInsideKeyframes(lineNum, allLines)) return null;\n if (/:\\s*[^;]*\\d+(\\.\\d+)?rem[;\\s]/.test(line)) {\n if (line.includes(\"calc(\") || line.includes(\"translateY(\")) {\n return null;\n }\n return \"Use --polly-space-* or --polly-text-* tokens instead of rem\";\n }\n return null;\n },\n },\n {\n id: \"no-hardcoded-border-width\",\n check: (line) => {\n if (isInsideComment(line)) return null;\n if (line.includes(\"var(--polly-border-width\")) return null;\n const m = line.match(/border(?:-(?:top|right|bottom|left))?:\\s*(\\d+)px\\s+solid/);\n if (!m?.[1]) return null;\n return \"Use a --polly-border-width-* token\";\n },\n },\n {\n id: \"no-hardcoded-border-radius\",\n check: (line) => {\n if (isInsideComment(line)) return null;\n if (line.includes(\"var(\")) return null;\n const m = line.match(/border-radius:\\s*(\\d+)px/);\n if (!m?.[1]) return null;\n if (m[1] === \"0\") return null;\n return \"Use a --polly-radius-{sm,md,lg} token\";\n },\n },\n {\n id: \"no-hardcoded-box-shadow\",\n check: (line) => {\n if (isInsideComment(line)) return null;\n if (!line.includes(\"box-shadow:\")) return null;\n if (line.includes(\"var(--polly-shadow\")) return null;\n if (line.includes(\"var(--polly-focus-ring\")) return null;\n if (line.includes(\"var(\")) return null;\n if (/box-shadow:\\s*none/.test(line)) return null;\n return \"Use var(--polly-shadow-*) or var(--polly-focus-ring)\";\n },\n },\n {\n id: \"no-hardcoded-font-weight\",\n check: (line) => {\n if (!/font-weight:\\s*\\d{3}/.test(line)) return null;\n if (line.includes(\"var(\")) return null;\n return \"Use var(--polly-weight-normal|medium|bold)\";\n },\n },\n];\n\nexport async function checkCssQuality(options: CssQualityOptions): Promise<CssCheckResult> {\n const rootDir = options.rootDir;\n const extensions = options.extensions ?? [\".module.css\"];\n const skipFiles = options.skipFiles ?? [\"theme.css\", \"tokens.css\"];\n const disabled = new Set(options.disableRules ?? []);\n const rules = DEFAULT_RULES.filter((r) => !disabled.has(r.id));\n const violations: CssViolation[] = [];\n\n await walkDirectory(\n rootDir,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: per-file nested rule loop; readable as written.\n async (full) => {\n if (!extensions.some((ext) => full.endsWith(ext))) return;\n const content = await Bun.file(full).text();\n const lines = content.split(\"\\n\");\n const hasFileIgnore = lines[0]?.includes(\"polly-ignore-all\");\n if (hasFileIgnore) return;\n\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (!line) continue;\n const trimmed = line.trim();\n if (trimmed === \"\" || trimmed === \"{\" || trimmed === \"}\") continue;\n for (const rule of rules) {\n const suggestion = rule.check(line, i, lines);\n if (suggestion) {\n violations.push({\n file: full,\n line: i + 1,\n rule: rule.id,\n content: trimmed,\n suggestion,\n });\n }\n }\n }\n },\n {\n rootDir,\n skipDirs: options.skipDirs,\n skipFiles,\n }\n );\n\n return makeResult(\"css-quality\", rootDir, violations);\n}\n",
|
|
9
|
+
"/**\n * Unused CSS detection (advisory).\n *\n * Finds classes and locally-defined custom properties in `.module.css`\n * files that appear to have no reference. Classes are checked against\n * every `.ts` / `.tsx` file's textual content; variables are checked\n * against other CSS files plus JS/TS referencing the same `--name`.\n * Matching is string-based and cannot see through dynamic lookups —\n * so this check is advisory only. Violations are returned as warnings;\n * the caller decides whether to fail the build.\n */\n\nimport type { CssCheckResult, CssViolation } from \"./shared.ts\";\nimport { makeResult, walkDirectory } from \"./shared.ts\";\n\nexport type CssUnusedOptions = {\n rootDir: string;\n /** Directory names skipped during recursion. */\n skipDirs?: string[];\n /** Class names treated as always-used (e.g. framework-generated). */\n alwaysUsedClasses?: string[];\n};\n\ntype Definition = {\n file: string;\n name: string;\n line: number;\n type: \"class\" | \"variable\";\n};\n\nexport async function checkCssUnused(options: CssUnusedOptions): Promise<CssCheckResult> {\n const rootDir = options.rootDir;\n const alwaysUsed = new Set(options.alwaysUsedClasses ?? []);\n const definitions: Definition[] = [];\n const tsContents: Array<{ file: string; content: string }> = [];\n const cssContents: Array<{ file: string; content: string }> = [];\n\n await walkDirectory(\n rootDir,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: filetype dispatch with nested matchAll scans; flat branches.\n async (full) => {\n if (full.endsWith(\".module.css\")) {\n const content = await Bun.file(full).text();\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (!line) continue;\n for (const m of line.matchAll(/(?:^|[\\s,>+~])\\.([\\w-]+)/g)) {\n if (m[1]) {\n definitions.push({\n file: full,\n name: m[1],\n line: i + 1,\n type: \"class\",\n });\n }\n }\n for (const m of line.matchAll(/^\\s+--([\\w-]+)\\s*:/g)) {\n if (m[1]) {\n definitions.push({\n file: full,\n name: `--${m[1]}`,\n line: i + 1,\n type: \"variable\",\n });\n }\n }\n }\n cssContents.push({ file: full, content });\n } else if (full.endsWith(\".css\") && !full.endsWith(\".css.d.ts\")) {\n const content = await Bun.file(full).text();\n cssContents.push({ file: full, content });\n } else if ((full.endsWith(\".ts\") || full.endsWith(\".tsx\")) && !full.endsWith(\".css.d.ts\")) {\n const content = await Bun.file(full).text();\n tsContents.push({ file: full, content });\n }\n },\n { rootDir, skipDirs: options.skipDirs }\n );\n\n // Deduplicate definitions (same class may appear in multiple selectors)\n const seen = new Set<string>();\n const uniqueDefs = definitions.filter((d) => {\n const key = `${d.file}:${d.type}:${d.name}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n\n function classReferenced(name: string): boolean {\n if (alwaysUsed.has(name)) return true;\n for (const { content } of tsContents) {\n if (\n content.includes(`.${name}`) ||\n content.includes(`['${name}']`) ||\n content.includes(`[\"${name}\"]`)\n ) {\n return true;\n }\n }\n return false;\n }\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: three-way reference check (other CSS, same CSS self-count, TS references).\n function variableReferenced(name: string, defFile: string): boolean {\n for (const { file, content } of cssContents) {\n if (file === defFile) continue;\n if (content.includes(`var(${name})`)) return true;\n }\n const self = cssContents.find((f) => f.file === defFile);\n if (self) {\n const occurrences = self.content.split(name).length - 1;\n if (occurrences > 1) return true;\n }\n for (const { content } of tsContents) {\n if (content.includes(name)) return true;\n }\n return false;\n }\n\n const violations: CssViolation[] = [];\n for (const def of uniqueDefs) {\n if (def.type === \"class\" && !classReferenced(def.name)) {\n violations.push({\n file: def.file,\n line: def.line,\n rule: \"unused-class\",\n content: `.${def.name}`,\n suggestion: \"Delete the selector or reference the class from a component\",\n });\n } else if (def.type === \"variable\" && !variableReferenced(def.name, def.file)) {\n violations.push({\n file: def.file,\n line: def.line,\n rule: \"unused-variable\",\n content: def.name,\n suggestion: \"Delete the definition or reference the variable\",\n });\n }\n }\n\n return makeResult(\"css-unused (advisory)\", rootDir, violations);\n}\n",
|
|
10
|
+
"/**\n * CSS variable reference validator.\n *\n * Two-pass scanner. First pass collects every `--name:` definition in\n * every `.css` file under the root. Second pass walks `.css`, `.ts`, and\n * `.tsx` files and reports any `var(--name)` that does not resolve. Catches\n * typos, stale references, and references to removed tokens.\n *\n * Variables that are set via JS (inline style) can be declared via\n * `dynamicVars` so they are treated as defined without appearing in CSS.\n */\n\nimport type { CssCheckResult, CssViolation } from \"./shared.ts\";\nimport { makeResult, walkDirectory } from \"./shared.ts\";\n\nexport type CssVarsOptions = {\n rootDir: string;\n /** Extensions scanned for var(--x) references. Default: css, ts, tsx. */\n scanExtensions?: string[];\n /** Variables defined dynamically (set via JS). Treated as defined. */\n dynamicVars?: string[];\n /** Directory names skipped during recursion. */\n skipDirs?: string[];\n};\n\nexport async function checkCssVars(options: CssVarsOptions): Promise<CssCheckResult> {\n const rootDir = options.rootDir;\n const scanExts = options.scanExtensions ?? [\".ts\", \".tsx\", \".css\"];\n const dynamic = new Set(options.dynamicVars ?? []);\n const definitions = new Set<string>(dynamic);\n const violations: CssViolation[] = [];\n\n // Pass 1 — collect all CSS custom property definitions\n await walkDirectory(\n rootDir,\n async (full) => {\n if (!full.endsWith(\".css\")) return;\n const content = await Bun.file(full).text();\n for (const m of content.matchAll(/--([\\w-]+)\\s*:/g)) {\n if (m[1]) definitions.add(`--${m[1]}`);\n }\n },\n { rootDir, skipDirs: options.skipDirs }\n );\n\n // Pass 2 — scan var(--name) references\n await walkDirectory(\n rootDir,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: nested for + matchAll per file is linear and readable as written.\n async (full) => {\n if (full.endsWith(\".css.d.ts\")) return;\n if (!scanExts.some((ext) => full.endsWith(ext))) return;\n const content = await Bun.file(full).text();\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (!line) continue;\n for (const m of line.matchAll(/var\\(--([\\w-]+)\\)/g)) {\n const name = `--${m[1]}`;\n if (!definitions.has(name)) {\n violations.push({\n file: full,\n line: i + 1,\n rule: \"undefined-var\",\n content: line.trim(),\n suggestion: `Define ${name} in a CSS file or add to dynamicVars`,\n });\n }\n }\n }\n },\n { rootDir, skipDirs: options.skipDirs }\n );\n\n return makeResult(\"css-vars\", rootDir, violations);\n}\n",
|
|
11
|
+
"/**\n * No-as-casting conformance check.\n *\n * Bans all TypeScript type assertions (`as Type`) except the allowed\n * patterns: `as const` (literal narrowing), `as unknown as` (explicit\n * escape hatch), import/export renames, and `as` inside strings or\n * comments. Violations include pattern-specific fix advice.\n *\n * This module exports the check logic as a library so consuming\n * applications can import it from `@fairfox/polly/quality` and run it\n * programmatically. Polly's own `scripts/check-no-as-casting.ts` is a\n * thin CLI wrapper around these exports.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { Glob } from \"bun\";\nimport { logger } from \"./logger\";\n\nexport interface Violation {\n file: string;\n line: number;\n content: string;\n advice?: string;\n}\n\nexport interface CheckResult {\n violations: Violation[];\n print: () => void;\n}\n\nexport interface CheckOptions {\n rootDir: string;\n exclude?: string[];\n excludePackages?: string[];\n excludeFiles?: string[];\n filePatterns?: string;\n}\n\n/**\n * Check whether a line contains a forbidden `as` type assertion.\n * Returns true if the line is clean (no violation), false if it violates.\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Source-text scanning with many skip rules is inherently branchy.\nexport function isLineClean(line: string): boolean {\n if (!line.includes(\" as \")) return true;\n\n const trimmed = line.trim();\n\n // Full-line comments\n if (trimmed.startsWith(\"//\") || trimmed.startsWith(\"*\") || trimmed.startsWith(\"/*\")) {\n return true;\n }\n\n // as const (literal narrowing)\n if (line.match(/\\bas const\\b/)) {\n const withoutAsConst = line.replace(/\\bas const\\b/g, \"\");\n if (!withoutAsConst.includes(\" as \")) return true;\n }\n\n // as unknown as (explicit escape hatch)\n if (line.includes(\" as unknown as \") || line.trimEnd().endsWith(\"as unknown as\")) {\n const withoutEscapeHatch = line.replace(/\\bas unknown as\\b/g, \"\");\n if (!withoutEscapeHatch.includes(\" as \")) return true;\n }\n\n // Import/export renames\n if (\n line.match(/\\b(import|export)\\s+.*\\s+as\\s+\\w+/) ||\n line.match(/\\b(import|export)\\s+\\*\\s+as\\s+\\w+/) ||\n line.match(/\\b(import|export)\\s+type\\s+.*\\s+as\\s+\\w+/) ||\n line.match(/^\\s*\\w+\\s+as\\s+\\w+,?\\s*$/) ||\n line.match(/^\\s*type\\s+\\w+\\s+as\\s+\\w+,?\\s*$/)\n ) {\n return true;\n }\n\n // Property declarations: as= or as: or as,\n if (line.match(/\\bas\\s*[=:,]/)) return true;\n\n // String literal detection: count quotes before each ` as ` occurrence.\n // If any quote type has an odd count, the ` as ` is inside a string.\n if (everyAsInsideString(line)) return true;\n\n // JSX text: ` as ` between > and < with no code syntax around it\n if (isJsxText(trimmed)) return true;\n\n // Plain text heuristic: indented line with no code syntax characters\n // before ` as ` — catches multiline JSX text and template literal bodies.\n if (isPlainText(trimmed)) return true;\n\n // Inline comment: ` as ` appears only after //\n const commentIdx = line.indexOf(\"//\");\n if (commentIdx >= 0 && line.indexOf(\" as \", commentIdx) >= 0) {\n const beforeComment = line.substring(0, commentIdx);\n if (!beforeComment.includes(\" as \")) return true;\n }\n\n // SQL alias: `) as column_name`\n if (line.match(/\"\\)\\s+as\\s+\\w+\"/)) return true;\n\n if (line.includes(\" satisfies \")) return true;\n\n return false;\n}\n\n/**\n * Returns true when every ` as ` occurrence in the line falls inside a\n * string literal (single-quoted, double-quoted, or backtick).\n */\nfunction everyAsInsideString(line: string): boolean {\n let searchFrom = 0;\n while (true) {\n const idx = line.indexOf(\" as \", searchFrom);\n if (idx < 0) return true; // no more ` as ` to check\n const before = line.substring(0, idx);\n const singleQuotes = (before.match(/'/g) ?? []).length;\n const doubleQuotes = (before.match(/\"/g) ?? []).length;\n const backticks = (before.match(/`/g) ?? []).length;\n if (singleQuotes % 2 === 0 && doubleQuotes % 2 === 0 && backticks % 2 === 0) {\n return false; // this ` as ` is outside any string\n }\n searchFrom = idx + 4;\n }\n}\n\n/**\n * Detects JSX text content — ` as ` appearing between > and < with no\n * code-like syntax around it (no braces, semicolons, equals signs).\n */\nfunction isJsxText(trimmed: string): boolean {\n // Classic JSX text: starts after > or is plain text ending before <\n if (trimmed.match(/^[^{};=()]*\\bas\\b[^{};=()]*$/)) {\n // No code syntax at all — could be JSX text or template literal body.\n // Reject if it looks like a type assertion (word ` as ` TypeName pattern\n // where TypeName starts with uppercase, or is a known TS type).\n if (\n !trimmed.match(/\\bas\\s+[A-Z]\\w*/) &&\n !trimmed.match(/\\bas\\s+(string|number|boolean|any|unknown|never)\\b/)\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Heuristic for plain text in template literals or JSX: the line has no\n * code-like characters before ` as ` — no `=`, `{`, `}`, `:`, `;`, `(`.\n */\nfunction isPlainText(trimmed: string): boolean {\n const idx = trimmed.indexOf(\" as \");\n if (idx < 0) return false;\n const before = trimmed.substring(0, idx);\n // If nothing before ` as ` looks like code, it's probably prose.\n return (\n !before.match(/[={}:;(]/) &&\n !before.match(/\\b(const|let|var|type|interface|function|return|await)\\b/)\n );\n}\n\n/**\n * Suggest a concrete fix for a specific violation pattern.\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Pattern-matching advice is a linear chain of if-returns.\nexport function suggestFix(line: string): string | undefined {\n if (line.includes(\"JSON.parse\")) {\n return \"Use a validation function or type guard to parse and validate the result.\";\n }\n if (\n line.includes(\"as HTMLInputElement\") ||\n line.includes(\"as HTMLTextAreaElement\") ||\n line.includes(\"as HTMLButtonElement\")\n ) {\n return \"Use instanceof: if (el instanceof HTMLInputElement) { el.value ... }\";\n }\n if (line.includes(\"as HTMLElement\") || line.includes(\"as Element\")) {\n return \"Use instanceof: if (el instanceof HTMLElement) { ... }\";\n }\n if (line.includes(\".doc()\") && line.includes(\"as \")) {\n return \"Type the DocHandle generic: repo.find<MyType>(id) returns DocHandle<MyType>.\";\n }\n if (\n line.includes(\"Record<string, unknown>\") &&\n (line.includes(\"window\") || line.includes(\"globalThis\"))\n ) {\n return \"Extract a type guard: function getGlobalProp(name: string): unknown { ... }\";\n }\n if (line.includes(\"Record<string, unknown>\")) {\n return \"Use a type guard function that narrows the unknown value to the target shape.\";\n }\n if (line.includes(\"as PeerId\") || line.includes(\"as DocumentId\")) {\n return \"Use the library's branded-type constructor if available, or centralise the cast in a factory.\";\n }\n if (line.includes(\"as string\") || line.includes(\"as number\") || line.includes(\"as boolean\")) {\n return \"Narrow with typeof: if (typeof x === 'string') { ... }\";\n }\n if (line.includes(\"as any\")) {\n return \"Replace 'any' with 'unknown' and add a type guard or validation at the boundary.\";\n }\n return undefined;\n}\n\nfunction isFileExcluded(\n relative: string,\n excludeDirs: Set<string>,\n excludePackages: Set<string>,\n excludeFiles: Set<string>\n): boolean {\n const segments = relative.split(\"/\");\n if (segments.some((s) => excludeDirs.has(s))) return true;\n if (excludePackages.size > 0 && segments.some((s) => excludePackages.has(s))) return true;\n const basename = segments[segments.length - 1] ?? \"\";\n return excludeFiles.has(basename) || excludeFiles.has(relative);\n}\n\nfunction findViolations(relative: string, content: string): Violation[] {\n const results: Violation[] = [];\n const lines = content.split(\"\\n\");\n let insideTemplate = false;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? \"\";\n const backticks = (line.match(/`/g) ?? []).length;\n const startedInTemplate = insideTemplate;\n if (backticks % 2 === 1) insideTemplate = !insideTemplate;\n\n // Line is entirely inside a multi-line template literal and has no\n // interpolation — treat as string content (e.g. SQL column aliases).\n if (startedInTemplate && backticks === 0 && !line.includes(\"${\")) continue;\n\n if (!isLineClean(line)) {\n results.push({\n file: relative,\n line: i + 1,\n content: line.trim(),\n advice: suggestFix(line.trim()),\n });\n }\n }\n return results;\n}\n\nfunction printViolations(violations: Violation[]): void {\n if (violations.length === 0) {\n logger.log(\"[no-as-casting] ✅ No violations found.\");\n return;\n }\n logger.log(`[no-as-casting] ❌ ${violations.length} violation(s) found:\\n`);\n for (const v of violations) {\n logger.log(` ${v.file}:${v.line}`);\n logger.log(` ${v.content}`);\n if (v.advice) logger.log(` 💡 ${v.advice}`);\n logger.log(\"\");\n }\n logger.log(\"[no-as-casting] Use type guards, validation, or fix the types at the source.\");\n logger.log('[no-as-casting] Only \"as const\" and \"as unknown as\" are allowed.');\n}\n\n/**\n * Run the no-as-casting check against a directory. Returns a result\n * object with the violations and a print function for CLI output.\n */\nexport async function checkNoAsCasting(options: CheckOptions): Promise<CheckResult> {\n const rootDir = options.rootDir;\n const excludeDirs = new Set(options.exclude ?? [\"node_modules\", \"dist\", \".git\", \".bun\"]);\n const excludePackages = new Set(options.excludePackages ?? []);\n const excludeFiles = new Set(options.excludeFiles ?? []);\n const pattern = options.filePatterns ?? \"**/*.{ts,tsx}\";\n const glob = new Glob(pattern);\n const violations: Violation[] = [];\n\n for await (const file of glob.scan({ cwd: rootDir, absolute: true })) {\n const relative = file.replace(`${rootDir}/`, \"\");\n if (isFileExcluded(relative, excludeDirs, excludePackages, excludeFiles)) continue;\n const content = readFileSync(file, \"utf-8\");\n violations.push(...findViolations(relative, content));\n }\n\n return { violations, print: () => printViolations(violations) };\n}\n",
|
|
12
|
+
"#!/usr/bin/env bun\n\n/**\n * CLI entry point for Polly quality checks.\n *\n * polly quality # run every check\n * polly quality no-as-casting # only the TS casting check\n * polly quality css # all CSS checks\n * polly quality css-quality # only hardcoded-values check\n * polly quality css-layout # only Layout-usage check\n * polly quality css-vars # only undefined-variable check\n * polly quality css-unused # only unused-selector check (advisory)\n *\n * Shared flags:\n * --root <dir> # defaults to process.cwd()\n * --exclude <a,b,c> # comma-separated dir names\n * --exclude-packages <a,b> # no-as-casting only\n * --exclude-files <a,b> # no-as-casting only\n * --pattern <glob> # no-as-casting only\n */\n\nimport {\n checkCssLayout,\n checkCssQuality,\n checkCssUnused,\n checkCssVars,\n checkNoAsCasting,\n} from \"./index\";\nimport { logger } from \"./logger\";\n\nconst args = process.argv.slice(2);\n\nfunction getFlag(name: string): string | undefined {\n const idx = args.indexOf(`--${name}`);\n return idx >= 0 ? args[idx + 1] : undefined;\n}\n\nfunction getSubcommand(): string {\n for (const arg of args) {\n if (!arg.startsWith(\"--\")) return arg;\n }\n return \"all\";\n}\n\nconst subcommand = getSubcommand();\nconst rootDir = getFlag(\"root\") ?? process.cwd();\nconst exclude = getFlag(\"exclude\")?.split(\",\") ?? [\n \"node_modules\",\n \"dist\",\n \".git\",\n \".bun\",\n \"dist-test\",\n \"build\",\n \"coverage\",\n];\nconst excludePackages = getFlag(\"exclude-packages\")?.split(\",\");\nconst excludeFiles = getFlag(\"exclude-files\")?.split(\",\");\nconst filePatterns = getFlag(\"pattern\");\n\nasync function runNoAsCasting(): Promise<number> {\n const result = await checkNoAsCasting({\n rootDir,\n exclude,\n ...(excludePackages ? { excludePackages } : {}),\n ...(excludeFiles ? { excludeFiles } : {}),\n ...(filePatterns ? { filePatterns } : {}),\n });\n result.print();\n return result.violations.length > 0 ? 1 : 0;\n}\n\nasync function runCssQuality(): Promise<number> {\n const r = await checkCssQuality({ rootDir, skipDirs: exclude });\n r.print();\n return r.violations.length > 0 ? 1 : 0;\n}\n\nasync function runCssLayout(): Promise<number> {\n const r = await checkCssLayout({ rootDir, skipDirs: exclude });\n r.print();\n return r.violations.length > 0 ? 1 : 0;\n}\n\nasync function runCssVars(): Promise<number> {\n const r = await checkCssVars({ rootDir, skipDirs: exclude });\n r.print();\n return r.violations.length > 0 ? 1 : 0;\n}\n\nasync function runCssUnused(): Promise<number> {\n const r = await checkCssUnused({ rootDir, skipDirs: exclude });\n r.print();\n return 0;\n}\n\nasync function runCssAll(): Promise<number> {\n const results = [\n await runCssQuality(),\n await runCssLayout(),\n await runCssVars(),\n await runCssUnused(),\n ];\n return results.some((code) => code !== 0) ? 1 : 0;\n}\n\nasync function runAll(): Promise<number> {\n const results = [await runNoAsCasting(), await runCssAll()];\n return results.some((code) => code !== 0) ? 1 : 0;\n}\n\nlet exitCode = 0;\nswitch (subcommand) {\n case \"no-as-casting\":\n exitCode = await runNoAsCasting();\n break;\n case \"css\":\n exitCode = await runCssAll();\n break;\n case \"css-quality\":\n exitCode = await runCssQuality();\n break;\n case \"css-layout\":\n exitCode = await runCssLayout();\n break;\n case \"css-vars\":\n exitCode = await runCssVars();\n break;\n case \"css-unused\":\n exitCode = await runCssUnused();\n break;\n case \"all\":\n exitCode = await runAll();\n break;\n default:\n logger.error(`Unknown quality subcommand: ${subcommand}`);\n logger.error(\n \"Expected one of: no-as-casting, css, css-quality, css-layout, css-vars, css-unused\"\n );\n exitCode = 2;\n}\n\nprocess.exit(exitCode);\n"
|
|
7
13
|
],
|
|
8
|
-
"mappings": "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",
|
|
9
|
-
"debugId": "
|
|
14
|
+
"mappings": "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",
|
|
15
|
+
"debugId": "33FEF60F0C8A440B64756E2164756E21",
|
|
10
16
|
"names": []
|
|
11
17
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS layout check.
|
|
3
|
+
*
|
|
4
|
+
* `display: flex` and `display: grid` are forbidden outside the Layout
|
|
5
|
+
* primitive. Apps express layout through the `<Layout>` component so
|
|
6
|
+
* layout decisions are declarative and greppable. Exempted paths default
|
|
7
|
+
* to `Layout.module.css`; consumers can extend the list through
|
|
8
|
+
* `layoutExemptPaths`. Suppress one-off cases with a layout-ignore CSS
|
|
9
|
+
* comment on the violating line or the line above.
|
|
10
|
+
*/
|
|
11
|
+
import type { CssCheckResult } from "./shared.ts";
|
|
12
|
+
export type CssLayoutOptions = {
|
|
13
|
+
rootDir: string;
|
|
14
|
+
/** File basenames or path fragments where layout CSS is permitted. */
|
|
15
|
+
layoutExemptPaths?: string[];
|
|
16
|
+
/** Directory names skipped during recursion. */
|
|
17
|
+
skipDirs?: string[];
|
|
18
|
+
};
|
|
19
|
+
export declare function checkCssLayout(options: CssLayoutOptions): Promise<CssCheckResult>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS quality check.
|
|
3
|
+
*
|
|
4
|
+
* Every styled value that has a semantic token must reference the token.
|
|
5
|
+
* Raw hex colours, rgba literals, rem values, numeric font-weights,
|
|
6
|
+
* magic z-indexes, hardcoded border-radius / border-width / box-shadow,
|
|
7
|
+
* inline transitions, and `!important` are all rejected. The rule set
|
|
8
|
+
* is intentionally opinionated; consumers who need an escape hatch may
|
|
9
|
+
* place a polly-ignore-all marker in a CSS comment on the first line
|
|
10
|
+
* of a file to skip checks for that file.
|
|
11
|
+
*/
|
|
12
|
+
import type { CssCheckResult } from "./shared.ts";
|
|
13
|
+
export type CssQualityOptions = {
|
|
14
|
+
rootDir: string;
|
|
15
|
+
/** Scan filter — files whose basename ends with one of these are checked. Default: `.module.css`. */
|
|
16
|
+
extensions?: string[];
|
|
17
|
+
/** File basenames skipped entirely. Default: `["theme.css", "tokens.css"]`. */
|
|
18
|
+
skipFiles?: string[];
|
|
19
|
+
/** Directory names skipped during recursion. */
|
|
20
|
+
skipDirs?: string[];
|
|
21
|
+
/** Which rules to disable by id. */
|
|
22
|
+
disableRules?: string[];
|
|
23
|
+
};
|
|
24
|
+
export declare function checkCssQuality(options: CssQualityOptions): Promise<CssCheckResult>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unused CSS detection (advisory).
|
|
3
|
+
*
|
|
4
|
+
* Finds classes and locally-defined custom properties in `.module.css`
|
|
5
|
+
* files that appear to have no reference. Classes are checked against
|
|
6
|
+
* every `.ts` / `.tsx` file's textual content; variables are checked
|
|
7
|
+
* against other CSS files plus JS/TS referencing the same `--name`.
|
|
8
|
+
* Matching is string-based and cannot see through dynamic lookups —
|
|
9
|
+
* so this check is advisory only. Violations are returned as warnings;
|
|
10
|
+
* the caller decides whether to fail the build.
|
|
11
|
+
*/
|
|
12
|
+
import type { CssCheckResult } from "./shared.ts";
|
|
13
|
+
export type CssUnusedOptions = {
|
|
14
|
+
rootDir: string;
|
|
15
|
+
/** Directory names skipped during recursion. */
|
|
16
|
+
skipDirs?: string[];
|
|
17
|
+
/** Class names treated as always-used (e.g. framework-generated). */
|
|
18
|
+
alwaysUsedClasses?: string[];
|
|
19
|
+
};
|
|
20
|
+
export declare function checkCssUnused(options: CssUnusedOptions): Promise<CssCheckResult>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS variable reference validator.
|
|
3
|
+
*
|
|
4
|
+
* Two-pass scanner. First pass collects every `--name:` definition in
|
|
5
|
+
* every `.css` file under the root. Second pass walks `.css`, `.ts`, and
|
|
6
|
+
* `.tsx` files and reports any `var(--name)` that does not resolve. Catches
|
|
7
|
+
* typos, stale references, and references to removed tokens.
|
|
8
|
+
*
|
|
9
|
+
* Variables that are set via JS (inline style) can be declared via
|
|
10
|
+
* `dynamicVars` so they are treated as defined without appearing in CSS.
|
|
11
|
+
*/
|
|
12
|
+
import type { CssCheckResult } from "./shared.ts";
|
|
13
|
+
export type CssVarsOptions = {
|
|
14
|
+
rootDir: string;
|
|
15
|
+
/** Extensions scanned for var(--x) references. Default: css, ts, tsx. */
|
|
16
|
+
scanExtensions?: string[];
|
|
17
|
+
/** Variables defined dynamically (set via JS). Treated as defined. */
|
|
18
|
+
dynamicVars?: string[];
|
|
19
|
+
/** Directory names skipped during recursion. */
|
|
20
|
+
skipDirs?: string[];
|
|
21
|
+
};
|
|
22
|
+
export declare function checkCssVars(options: CssVarsOptions): Promise<CssCheckResult>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared plumbing for CSS conformance checks.
|
|
3
|
+
*
|
|
4
|
+
* File discovery, violation printing, and common scanner options live
|
|
5
|
+
* here so each check (`quality`, `layout`, `vars`, `unused`) can stay
|
|
6
|
+
* focused on its own rule set.
|
|
7
|
+
*/
|
|
8
|
+
export type CssViolation = {
|
|
9
|
+
file: string;
|
|
10
|
+
line: number;
|
|
11
|
+
rule: string;
|
|
12
|
+
content: string;
|
|
13
|
+
suggestion: string;
|
|
14
|
+
};
|
|
15
|
+
export type CssCheckResult = {
|
|
16
|
+
violations: CssViolation[];
|
|
17
|
+
print: () => void;
|
|
18
|
+
};
|
|
19
|
+
export type CssScanOptions = {
|
|
20
|
+
rootDir: string;
|
|
21
|
+
/** Directory names (not paths) to skip during recursion. */
|
|
22
|
+
skipDirs?: string[];
|
|
23
|
+
/** File names (not paths) to skip entirely. */
|
|
24
|
+
skipFiles?: string[];
|
|
25
|
+
};
|
|
26
|
+
export declare const DEFAULT_SKIP_DIRS: string[];
|
|
27
|
+
export declare function walkDirectory(dir: string, visit: (filePath: string, relPath: string) => Promise<void>, opts: CssScanOptions): Promise<void>;
|
|
28
|
+
export declare function formatViolations(kind: string, violations: CssViolation[], rootDir: string): string[];
|
|
29
|
+
export declare function makeResult(kind: string, rootDir: string, violations: CssViolation[]): CssCheckResult;
|
|
30
|
+
/** True when the line is a CSS comment (standalone or continuation). */
|
|
31
|
+
export declare function isInsideComment(line: string): boolean;
|
|
32
|
+
/** Heuristic: true when the given line number falls inside an @keyframes block. */
|
|
33
|
+
export declare function isInsideKeyframes(lineNum: number, allLines: readonly string[]): boolean;
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
* applications wire it into their own CI or pre-commit hook via the
|
|
8
8
|
* companion `checkNoAsCasting` runner.
|
|
9
9
|
*
|
|
10
|
+
* The CSS conformance family — `checkCssQuality`, `checkCssLayout`,
|
|
11
|
+
* `checkCssVars`, `checkCssUnused` — enforces the token-driven styling
|
|
12
|
+
* contract that @fairfox/polly/ui ships: all styled values go through
|
|
13
|
+
* semantic tokens, layout goes through the `<Layout>` primitive, no
|
|
14
|
+
* variable references dangle, and unused selectors are surfaced.
|
|
15
|
+
*
|
|
10
16
|
* @example
|
|
11
17
|
* ```typescript
|
|
12
18
|
* import { checkNoAsCasting } from "@fairfox/polly/quality";
|
|
@@ -22,4 +28,10 @@
|
|
|
22
28
|
* }
|
|
23
29
|
* ```
|
|
24
30
|
*/
|
|
31
|
+
export { type CssLayoutOptions, checkCssLayout, } from "./css/check-layout.ts";
|
|
32
|
+
export { type CssQualityOptions, checkCssQuality, } from "./css/check-quality.ts";
|
|
33
|
+
export { type CssUnusedOptions, checkCssUnused } from "./css/check-unused.ts";
|
|
34
|
+
export { type CssVarsOptions, checkCssVars } from "./css/check-vars.ts";
|
|
35
|
+
export type { CssCheckResult, CssViolation } from "./css/shared.ts";
|
|
36
|
+
export { logger, type QualityLogger, resetLogger, } from "./logger.ts";
|
|
25
37
|
export { type CheckResult, checkNoAsCasting, isLineClean, suggestFix, type Violation, } from "./no-as-casting";
|