safeword 0.15.9 → 0.15.11

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 (62) hide show
  1. package/dist/{check-LOVNOEUC.js → check-CUCQRUXT.js} +4 -4
  2. package/dist/{chunk-JLOQFDEL.js → chunk-4QVNEEYP.js} +2 -2
  3. package/dist/{chunk-JLOQFDEL.js.map → chunk-4QVNEEYP.js.map} +1 -1
  4. package/dist/{chunk-NUKQPPCM.js → chunk-NXSSFAFS.js} +45 -6
  5. package/dist/chunk-NXSSFAFS.js.map +1 -0
  6. package/dist/{chunk-OYBKTOVF.js → chunk-PMYWVDOF.js} +53 -43
  7. package/dist/chunk-PMYWVDOF.js.map +1 -0
  8. package/dist/{chunk-EGI2VVJ4.js → chunk-SO45XRNO.js} +185 -90
  9. package/dist/chunk-SO45XRNO.js.map +1 -0
  10. package/dist/{chunk-ZBTABXIC.js → chunk-UCZB27KH.js} +2 -2
  11. package/dist/cli.js +7 -7
  12. package/dist/cli.js.map +1 -1
  13. package/dist/{diff-P66MAGJQ.js → diff-MF42ROSX.js} +3 -3
  14. package/dist/index.d.ts +2 -2
  15. package/dist/index.js +2 -2
  16. package/dist/presets/typescript/index.d.ts +68 -7
  17. package/dist/presets/typescript/index.js +8 -2
  18. package/dist/{reset-FD5WGVGB.js → reset-GOAEEMD6.js} +3 -3
  19. package/dist/{setup-I63R6JD2.js → setup-TISRKM2F.js} +5 -5
  20. package/dist/{sync-config-SJ2HBZM6.js → sync-config-GPYJLOW5.js} +2 -2
  21. package/dist/{upgrade-HG7TZBLZ.js → upgrade-MI34U4M6.js} +4 -4
  22. package/package.json +6 -1
  23. package/templates/.jscpd.json +1 -1
  24. package/templates/SAFEWORD.md +4 -30
  25. package/templates/cursor/rules/bdd-core.mdc +52 -0
  26. package/templates/cursor/rules/bdd-decomposition.mdc +43 -0
  27. package/templates/cursor/rules/bdd-discovery.mdc +46 -0
  28. package/templates/cursor/rules/bdd-done.mdc +44 -0
  29. package/templates/cursor/rules/bdd-scenarios.mdc +50 -0
  30. package/templates/cursor/rules/bdd-splitting.mdc +51 -0
  31. package/templates/cursor/rules/bdd-tdd.mdc +55 -0
  32. package/templates/cursor/rules/safeword-debugging.mdc +7 -0
  33. package/templates/cursor/rules/safeword-quality-reviewing.mdc +0 -1
  34. package/templates/guides/architecture-guide.md +0 -38
  35. package/templates/guides/cli-reference.md +16 -14
  36. package/templates/guides/context-files-guide.md +1 -14
  37. package/templates/guides/design-doc-guide.md +0 -12
  38. package/templates/guides/planning-guide.md +1 -18
  39. package/templates/guides/testing-guide.md +13 -44
  40. package/templates/hooks/stop-quality.ts +97 -34
  41. package/templates/skills/safeword-bdd-orchestrating/DECOMPOSITION.md +44 -0
  42. package/templates/skills/safeword-bdd-orchestrating/DISCOVERY.md +59 -0
  43. package/templates/skills/safeword-bdd-orchestrating/DONE.md +44 -0
  44. package/templates/skills/safeword-bdd-orchestrating/SCENARIOS.md +59 -0
  45. package/templates/skills/safeword-bdd-orchestrating/SKILL.md +26 -548
  46. package/templates/skills/safeword-bdd-orchestrating/SPLITTING.md +56 -0
  47. package/templates/skills/safeword-bdd-orchestrating/TDD.md +69 -0
  48. package/templates/skills/safeword-debugging/SKILL.md +19 -0
  49. package/templates/skills/safeword-quality-reviewing/SKILL.md +34 -116
  50. package/dist/chunk-EGI2VVJ4.js.map +0 -1
  51. package/dist/chunk-NUKQPPCM.js.map +0 -1
  52. package/dist/chunk-OYBKTOVF.js.map +0 -1
  53. package/templates/cursor/rules/safeword-bdd-orchestrating.mdc +0 -628
  54. package/templates/guides/code-philosophy.md +0 -207
  55. package/templates/prompts/quality-review.md +0 -10
  56. /package/dist/{check-LOVNOEUC.js.map → check-CUCQRUXT.js.map} +0 -0
  57. /package/dist/{chunk-ZBTABXIC.js.map → chunk-UCZB27KH.js.map} +0 -0
  58. /package/dist/{diff-P66MAGJQ.js.map → diff-MF42ROSX.js.map} +0 -0
  59. /package/dist/{reset-FD5WGVGB.js.map → reset-GOAEEMD6.js.map} +0 -0
  60. /package/dist/{setup-I63R6JD2.js.map → setup-TISRKM2F.js.map} +0 -0
  61. /package/dist/{sync-config-SJ2HBZM6.js.map → sync-config-GPYJLOW5.js.map} +0 -0
  62. /package/dist/{upgrade-HG7TZBLZ.js.map → upgrade-MI34U4M6.js.map} +0 -0
@@ -3,13 +3,13 @@ import {
3
3
  } from "./chunk-FJYRWU2V.js";
4
4
  import {
5
5
  getMissingPacks
6
- } from "./chunk-ZBTABXIC.js";
6
+ } from "./chunk-UCZB27KH.js";
7
7
  import {
8
8
  SAFEWORD_SCHEMA,
9
9
  createProjectContext,
10
10
  reconcile
11
- } from "./chunk-EGI2VVJ4.js";
12
- import "./chunk-OYBKTOVF.js";
11
+ } from "./chunk-SO45XRNO.js";
12
+ import "./chunk-PMYWVDOF.js";
13
13
  import {
14
14
  VERSION
15
15
  } from "./chunk-ORQHKDT2.js";
@@ -189,4 +189,4 @@ async function check(options) {
189
189
  export {
190
190
  check
191
191
  };
192
- //# sourceMappingURL=check-LOVNOEUC.js.map
192
+ //# sourceMappingURL=check-CUCQRUXT.js.map
@@ -209,7 +209,7 @@ function generateDepCruiseMainConfig() {
209
209
  return `/**
210
210
  * Dependency Cruiser Configuration
211
211
  *
212
- * Imports auto-generated rules from .safeword/depcruise-config.js
212
+ * Imports auto-generated rules from .safeword/depcruise-config.cjs
213
213
  * ADD YOUR CUSTOM RULES BELOW the spread operator.
214
214
  */
215
215
 
@@ -280,4 +280,4 @@ export {
280
280
  hasArchitectureDetected,
281
281
  syncConfig
282
282
  };
283
- //# sourceMappingURL=chunk-JLOQFDEL.js.map
283
+ //# sourceMappingURL=chunk-4QVNEEYP.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/sync-config.ts","../src/utils/boundaries.ts","../src/utils/depcruise-config.ts"],"sourcesContent":["/**\n * Sync Config command - Regenerate depcruise config from current project structure\n *\n * Used by `/audit` slash command to refresh config before running checks.\n */\n\nimport { writeFileSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport { detectArchitecture } from '../utils/boundaries.js';\nimport {\n type DepCruiseArchitecture,\n detectWorkspaces,\n generateDepCruiseConfigFile,\n generateDepCruiseMainConfig,\n} from '../utils/depcruise-config.js';\nimport { exists } from '../utils/fs.js';\nimport { error, info, success } from '../utils/output.js';\n\ninterface SyncConfigResult {\n generatedConfig: boolean;\n createdMainConfig: boolean;\n}\n\n/**\n * Core sync logic - writes depcruise configs to disk\n * Can be called from setup or as standalone command\n */\nexport function syncConfigCore(cwd: string, arch: DepCruiseArchitecture): SyncConfigResult {\n const safewordDirectory = nodePath.join(cwd, '.safeword');\n const result: SyncConfigResult = {\n generatedConfig: false,\n createdMainConfig: false,\n };\n\n // Generate and write .safeword/depcruise-config.cjs (CJS for compatibility)\n const generatedConfigPath = nodePath.join(safewordDirectory, 'depcruise-config.cjs');\n const generatedConfig = generateDepCruiseConfigFile(arch);\n writeFileSync(generatedConfigPath, generatedConfig);\n result.generatedConfig = true;\n\n // Create main config if not exists (self-healing)\n // Use .cjs extension to work in ESM projects (type: \"module\")\n const mainConfigPath = nodePath.join(cwd, '.dependency-cruiser.cjs');\n if (!exists(mainConfigPath)) {\n const mainConfig = generateDepCruiseMainConfig();\n writeFileSync(mainConfigPath, mainConfig);\n result.createdMainConfig = true;\n }\n\n return result;\n}\n\n/**\n * Build full architecture info by combining detected layers with workspaces\n */\nexport function buildArchitecture(cwd: string): DepCruiseArchitecture {\n const arch = detectArchitecture(cwd);\n const workspaces = detectWorkspaces(cwd);\n return { ...arch, workspaces };\n}\n\n/**\n * Check if architecture was detected (layers, monorepo structure, or workspaces)\n */\nexport function hasArchitectureDetected(arch: DepCruiseArchitecture): boolean {\n return arch.elements.length > 0 || arch.isMonorepo || (arch.workspaces?.length ?? 0) > 0;\n}\n\n/**\n * CLI command: Sync depcruise config with current project structure\n */\nexport async function syncConfig(): Promise<void> {\n const cwd = process.cwd();\n const safewordDirectory = nodePath.join(cwd, '.safeword');\n\n // Check if .safeword exists\n if (!exists(safewordDirectory)) {\n error('Not configured. Run `safeword setup` first.');\n process.exit(1);\n }\n\n // Detect current architecture and workspaces\n const arch = buildArchitecture(cwd);\n const result = syncConfigCore(cwd, arch);\n\n if (result.generatedConfig) {\n info('Generated .safeword/depcruise-config.cjs');\n }\n if (result.createdMainConfig) {\n info('Created .dependency-cruiser.cjs');\n }\n\n success('Config synced');\n}\n","/**\n * Architecture boundaries detection and config generation\n *\n * Auto-detects common architecture directories and generates\n * eslint-plugin-boundaries config with sensible hierarchy rules.\n *\n * Supports:\n * - Standard projects (src/utils, utils/)\n * - Monorepos (packages/*, apps/*)\n * - Various naming conventions (helpers, shared, core, etc.)\n */\n\nimport { readdirSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport { exists } from './fs.js';\n\n/**\n * Architecture layer definitions with alternative names.\n * Each layer maps to equivalent directory names.\n * Order defines hierarchy: earlier = lower layer.\n */\nconst ARCHITECTURE_LAYERS = [\n // Layer 0: Pure types (no imports)\n { layer: 'types', dirs: ['types', 'interfaces', 'schemas'] },\n // Layer 1: Utilities (only types)\n { layer: 'utils', dirs: ['utils', 'helpers', 'shared', 'common', 'core'] },\n // Layer 2: Libraries (types, utils)\n { layer: 'lib', dirs: ['lib', 'libraries'] },\n // Layer 3: State & logic (types, utils, lib)\n { layer: 'hooks', dirs: ['hooks', 'composables'] },\n { layer: 'services', dirs: ['services', 'api', 'stores', 'state'] },\n // Layer 4: UI components (all above)\n { layer: 'components', dirs: ['components', 'ui'] },\n // Layer 5: Features (all above)\n { layer: 'features', dirs: ['features', 'modules', 'domains'] },\n // Layer 6: Entry points (can import everything)\n { layer: 'app', dirs: ['app', 'pages', 'views', 'routes', 'commands'] },\n] as const;\n\ntype Layer = (typeof ARCHITECTURE_LAYERS)[number]['layer'];\n\n/**\n * Hierarchy rules: what each layer can import\n * Lower layers have fewer import permissions\n */\nconst HIERARCHY: Record<Layer, Layer[]> = {\n types: [],\n utils: ['types'],\n lib: ['utils', 'types'],\n hooks: ['lib', 'utils', 'types'],\n services: ['lib', 'utils', 'types'],\n components: ['hooks', 'services', 'lib', 'utils', 'types'],\n features: ['components', 'hooks', 'services', 'lib', 'utils', 'types'],\n app: ['features', 'components', 'hooks', 'services', 'lib', 'utils', 'types'],\n};\n\ninterface DetectedElement {\n layer: Layer;\n pattern: string; // glob pattern for boundaries config\n location: string; // human-readable location\n}\n\nexport interface DetectedArchitecture {\n elements: DetectedElement[];\n isMonorepo: boolean;\n}\n\n/**\n * Find monorepo package directories\n * @param projectDirectory\n */\nfunction findMonorepoPackages(projectDirectory: string): string[] {\n const packages: string[] = [];\n\n // Check common monorepo patterns\n const monorepoRoots = ['packages', 'apps', 'libs', 'modules'];\n\n for (const root of monorepoRoots) {\n const rootPath = nodePath.join(projectDirectory, root);\n if (!exists(rootPath)) continue;\n\n try {\n const entries = readdirSync(rootPath, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('.')) {\n packages.push(nodePath.join(root, entry.name));\n }\n }\n } catch {\n // Directory not readable, skip\n }\n }\n\n return packages;\n}\n\n/**\n * Check if a layer already exists for this path prefix\n * @param elements\n * @param layer\n * @param pathPrefix\n */\nfunction hasLayerForPrefix(elements: DetectedElement[], layer: Layer, pathPrefix: string): boolean {\n return elements.some(\n element => element.layer === layer && element.pattern.startsWith(pathPrefix),\n );\n}\n\n/**\n * Scan a single search path for architecture layers\n * @param projectDirectory\n * @param searchPath\n * @param pathPrefix\n * @param elements\n */\nfunction scanSearchPath(\n projectDirectory: string,\n searchPath: string,\n pathPrefix: string,\n elements: DetectedElement[],\n): void {\n for (const layerDefinition of ARCHITECTURE_LAYERS) {\n for (const dirName of layerDefinition.dirs) {\n const fullPath = nodePath.join(projectDirectory, searchPath, dirName);\n if (exists(fullPath) && !hasLayerForPrefix(elements, layerDefinition.layer, pathPrefix)) {\n elements.push({\n layer: layerDefinition.layer,\n pattern: `${pathPrefix}${dirName}/**`,\n location: `${pathPrefix}${dirName}`,\n });\n }\n }\n }\n}\n\n/**\n * Scan a directory for architecture layers\n * @param projectDirectory\n * @param basePath\n */\nfunction scanForLayers(projectDirectory: string, basePath: string): DetectedElement[] {\n const elements: DetectedElement[] = [];\n const prefix = basePath ? `${basePath}/` : '';\n\n // Check src/ and root level\n scanSearchPath(projectDirectory, nodePath.join(basePath, 'src'), `${prefix}src/`, elements);\n scanSearchPath(projectDirectory, basePath, prefix, elements);\n\n return elements;\n}\n\n/**\n * Detects architecture directories in the project\n * Handles both standard projects and monorepos\n * @param projectDirectory\n */\nexport function detectArchitecture(projectDirectory: string): DetectedArchitecture {\n const elements: DetectedElement[] = [];\n\n // First, check for monorepo packages\n const packages = findMonorepoPackages(projectDirectory);\n const isMonorepo = packages.length > 0;\n\n if (isMonorepo) {\n // Scan each package\n for (const pkg of packages) {\n elements.push(...scanForLayers(projectDirectory, pkg));\n }\n }\n\n // Also scan root level (works for both monorepo root and standard projects)\n elements.push(...scanForLayers(projectDirectory, ''));\n\n // Deduplicate by pattern\n const seen = new Set<string>();\n const uniqueElements = elements.filter(element => {\n if (seen.has(element.pattern)) return false;\n seen.add(element.pattern);\n return true;\n });\n\n return { elements: uniqueElements, isMonorepo };\n}\n\n/**\n * Format a single element for the config\n * @param el\n */\nfunction formatElement(element: DetectedElement): string {\n return ` { type: '${element.layer}', pattern: '${element.pattern}', mode: 'full' }`;\n}\n\n/**\n * Format allowed imports for a rule\n * @param allowed\n */\nfunction formatAllowedImports(allowed: Layer[]): string {\n return allowed.map(d => `'${d}'`).join(', ');\n}\n\n/**\n * Generate a single rule for what a layer can import\n * @param layer\n * @param detectedLayers\n */\nfunction generateRule(layer: Layer, detectedLayers: Set<Layer>): string | undefined {\n const allowedLayers = HIERARCHY[layer];\n if (allowedLayers.length === 0) return undefined;\n\n const allowed = allowedLayers.filter(dep => detectedLayers.has(dep));\n if (allowed.length === 0) return undefined;\n\n return ` { from: ['${layer}'], allow: [${formatAllowedImports(allowed)}] }`;\n}\n\n/**\n * Build description of what was detected\n * @param arch\n */\nfunction buildDetectedInfo(arch: DetectedArchitecture): string {\n if (arch.elements.length === 0) {\n return 'No architecture directories detected yet - add types/, utils/, components/, etc.';\n }\n const locations = arch.elements.map(element => element.location).join(', ');\n const monorepoNote = arch.isMonorepo ? ' (monorepo)' : '';\n return `Detected: ${locations}${monorepoNote}`;\n}\n\n/**\n *\n * @param arch\n */\nexport function generateBoundariesConfig(arch: DetectedArchitecture): string {\n const hasElements = arch.elements.length > 0;\n\n // Generate element definitions\n const elementsContent = arch.elements.map(element => formatElement(element)).join(',\\n');\n\n // Generate rules (what each layer can import)\n const detectedLayers = new Set(arch.elements.map(element => element.layer));\n const rules = [...detectedLayers]\n .map(layer => generateRule(layer, detectedLayers))\n .filter((rule): rule is string => rule !== undefined);\n const rulesContent = rules.join(',\\n');\n\n const detectedInfo = buildDetectedInfo(arch);\n\n return `/**\n * Architecture Boundaries Configuration (AUTO-GENERATED)\n *\n * ${detectedInfo}\n *\n * This enforces import boundaries between architectural layers:\n * - Lower layers (types, utils) cannot import from higher layers (components, features)\n * - Uses 'error' severity - LLMs ignore warnings, errors force compliance\n *\n * Recognized directories (in hierarchy order):\n * types → utils → lib → hooks/services → components → features/modules → app\n *\n * To customize, override in your eslint.config.mjs:\n * rules: { 'boundaries/element-types': ['error', { ... }] }\n */\n\nimport boundaries from 'eslint-plugin-boundaries';\n\nexport default {\n plugins: { boundaries },\n settings: {\n 'boundaries/elements': [\n${elementsContent}\n ],\n },\n rules: {${\n hasElements\n ? `\n 'boundaries/element-types': ['error', {\n default: 'disallow',\n rules: [\n${rulesContent}\n ],\n }],`\n : ''\n }\n 'boundaries/no-unknown': 'off', // Allow files outside defined elements\n 'boundaries/no-unknown-files': 'off', // Allow non-matching files\n },\n};\n`;\n}\n","/**\n * Dependency-cruiser config generator\n *\n * Generates dependency-cruiser configuration from detected architecture.\n * Used by `safeword sync-config` command and `/audit` slash command.\n */\n\nimport nodePath from 'node:path';\n\nimport type { DetectedArchitecture } from './boundaries.js';\nimport { readJson } from './fs.js';\n\nexport interface DepCruiseArchitecture extends DetectedArchitecture {\n workspaces?: string[];\n}\n\ninterface PackageJson {\n workspaces?: string[] | { packages?: string[] };\n}\n\n/**\n * Detect workspaces from package.json\n * Supports both array format and object format (yarn workspaces)\n */\nexport function detectWorkspaces(cwd: string): string[] | undefined {\n const packageJsonPath = nodePath.join(cwd, 'package.json');\n const packageJson = readJson(packageJsonPath) as PackageJson | undefined;\n\n if (!packageJson?.workspaces) return undefined;\n\n // Handle both formats: string[] or { packages: string[] }\n const workspaces = Array.isArray(packageJson.workspaces)\n ? packageJson.workspaces\n : packageJson.workspaces.packages;\n\n return workspaces && workspaces.length > 0 ? workspaces : undefined;\n}\n\n/**\n * Generate monorepo hierarchy rules based on workspace patterns\n */\nfunction generateMonorepoRules(workspaces: string[]): string {\n const rules: string[] = [];\n\n const hasLibs = workspaces.some(w => w.startsWith('libs'));\n const hasPackages = workspaces.some(w => w.startsWith('packages'));\n const hasApps = workspaces.some(w => w.startsWith('apps'));\n\n // libs cannot import packages or apps\n if (hasLibs && (hasPackages || hasApps)) {\n rules.push(` {\n name: 'libs-cannot-import-packages-or-apps',\n severity: 'error',\n from: { path: '^libs/' },\n to: { path: '^(packages|apps)/' },\n }`);\n }\n\n // packages cannot import apps\n if (hasPackages && hasApps) {\n rules.push(` {\n name: 'packages-cannot-import-apps',\n severity: 'error',\n from: { path: '^packages/' },\n to: { path: '^apps/' },\n }`);\n }\n\n return rules.join(',\\n');\n}\n\n/**\n * Generate .safeword/depcruise-config.js content (forbidden rules + options)\n */\nexport function generateDepCruiseConfigFile(arch: DepCruiseArchitecture): string {\n const monorepoRules = arch.workspaces ? generateMonorepoRules(arch.workspaces) : '';\n const hasMonorepoRules = monorepoRules.length > 0;\n\n return String.raw`module.exports = {\n forbidden: [\n // =========================================================================\n // ERROR RULES (block on violations)\n // =========================================================================\n {\n name: 'no-circular',\n comment: 'Circular dependencies cause runtime issues and make code hard to reason about',\n severity: 'error',\n from: {},\n to: { circular: true },\n },\n {\n name: 'no-deprecated-deps',\n comment: 'Deprecated npm packages should be replaced - they may have security issues or be unmaintained',\n severity: 'error',\n from: {},\n to: { dependencyTypes: ['deprecated'] },\n },${hasMonorepoRules ? `\\n${monorepoRules},` : ''}\n\n // =========================================================================\n // WARNING RULES (flag issues but don't block)\n // =========================================================================\n {\n name: 'no-dev-deps-in-src',\n comment: 'Production code should not import devDependencies - may cause runtime failures',\n severity: 'warn',\n from: {\n path: ['^src', '^packages/[^/]+/src'],\n pathNot: '\\\\.test\\\\.[tj]sx?$',\n },\n to: { dependencyTypes: ['npm-dev'] },\n },\n {\n name: 'no-orphans',\n comment: 'Orphan modules are not imported anywhere - may be dead code',\n severity: 'warn',\n from: {\n orphan: true,\n pathNot: [\n // Entry points\n '(^|/)index\\\\.[tj]sx?$',\n '(^|/)main\\\\.[tj]sx?$',\n '(^|/)cli\\\\.[tj]s$',\n '\\\\.config\\\\.[tj]s$',\n '\\\\.config\\\\.mjs$',\n // Test files\n '\\\\.test\\\\.[tj]sx?$',\n '\\\\.spec\\\\.[tj]sx?$',\n '/tests/',\n '/__tests__/',\n // Astro/Next.js pages and content\n '/src/content/',\n '/src/pages/',\n '/app/',\n ],\n },\n to: {},\n },\n ],\n options: {\n doNotFollow: { path: ['node_modules', '.safeword'] },\n exclude: {\n path: ['node_modules', 'dist', 'build', 'coverage', '\\\\.d\\\\.ts$'],\n },\n tsPreCompilationDeps: true,\n tsConfig: { fileName: 'tsconfig.json' },\n enhancedResolveOptions: {\n extensions: ['.ts', '.tsx', '.js', '.jsx'],\n exportsFields: ['exports'],\n conditionNames: ['import', 'require', 'node', 'default'],\n },\n },\n};\n`;\n}\n\n/**\n * Generate .dependency-cruiser.js (main config that imports generated)\n */\nexport function generateDepCruiseMainConfig(): string {\n return `/**\n * Dependency Cruiser Configuration\n *\n * Imports auto-generated rules from .safeword/depcruise-config.js\n * ADD YOUR CUSTOM RULES BELOW the spread operator.\n */\n\nconst generated = require('./.safeword/depcruise-config.cjs');\n\nmodule.exports = {\n forbidden: [\n ...generated.forbidden,\n // ADD YOUR CUSTOM RULES BELOW:\n // { name: 'no-legacy', from: { path: 'legacy/' }, to: { path: 'new/' } },\n ],\n options: {\n ...generated.options,\n // Your overrides here\n },\n};\n`;\n}\n"],"mappings":"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","names":["nodePath","nodePath","nodePath","nodePath"]}
1
+ {"version":3,"sources":["../src/commands/sync-config.ts","../src/utils/boundaries.ts","../src/utils/depcruise-config.ts"],"sourcesContent":["/**\n * Sync Config command - Regenerate depcruise config from current project structure\n *\n * Used by `/audit` slash command to refresh config before running checks.\n */\n\nimport { writeFileSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport { detectArchitecture } from '../utils/boundaries.js';\nimport {\n type DepCruiseArchitecture,\n detectWorkspaces,\n generateDepCruiseConfigFile,\n generateDepCruiseMainConfig,\n} from '../utils/depcruise-config.js';\nimport { exists } from '../utils/fs.js';\nimport { error, info, success } from '../utils/output.js';\n\ninterface SyncConfigResult {\n generatedConfig: boolean;\n createdMainConfig: boolean;\n}\n\n/**\n * Core sync logic - writes depcruise configs to disk\n * Can be called from setup or as standalone command\n */\nexport function syncConfigCore(cwd: string, arch: DepCruiseArchitecture): SyncConfigResult {\n const safewordDirectory = nodePath.join(cwd, '.safeword');\n const result: SyncConfigResult = {\n generatedConfig: false,\n createdMainConfig: false,\n };\n\n // Generate and write .safeword/depcruise-config.cjs (CJS for compatibility)\n const generatedConfigPath = nodePath.join(safewordDirectory, 'depcruise-config.cjs');\n const generatedConfig = generateDepCruiseConfigFile(arch);\n writeFileSync(generatedConfigPath, generatedConfig);\n result.generatedConfig = true;\n\n // Create main config if not exists (self-healing)\n // Use .cjs extension to work in ESM projects (type: \"module\")\n const mainConfigPath = nodePath.join(cwd, '.dependency-cruiser.cjs');\n if (!exists(mainConfigPath)) {\n const mainConfig = generateDepCruiseMainConfig();\n writeFileSync(mainConfigPath, mainConfig);\n result.createdMainConfig = true;\n }\n\n return result;\n}\n\n/**\n * Build full architecture info by combining detected layers with workspaces\n */\nexport function buildArchitecture(cwd: string): DepCruiseArchitecture {\n const arch = detectArchitecture(cwd);\n const workspaces = detectWorkspaces(cwd);\n return { ...arch, workspaces };\n}\n\n/**\n * Check if architecture was detected (layers, monorepo structure, or workspaces)\n */\nexport function hasArchitectureDetected(arch: DepCruiseArchitecture): boolean {\n return arch.elements.length > 0 || arch.isMonorepo || (arch.workspaces?.length ?? 0) > 0;\n}\n\n/**\n * CLI command: Sync depcruise config with current project structure\n */\nexport async function syncConfig(): Promise<void> {\n const cwd = process.cwd();\n const safewordDirectory = nodePath.join(cwd, '.safeword');\n\n // Check if .safeword exists\n if (!exists(safewordDirectory)) {\n error('Not configured. Run `safeword setup` first.');\n process.exit(1);\n }\n\n // Detect current architecture and workspaces\n const arch = buildArchitecture(cwd);\n const result = syncConfigCore(cwd, arch);\n\n if (result.generatedConfig) {\n info('Generated .safeword/depcruise-config.cjs');\n }\n if (result.createdMainConfig) {\n info('Created .dependency-cruiser.cjs');\n }\n\n success('Config synced');\n}\n","/**\n * Architecture boundaries detection and config generation\n *\n * Auto-detects common architecture directories and generates\n * eslint-plugin-boundaries config with sensible hierarchy rules.\n *\n * Supports:\n * - Standard projects (src/utils, utils/)\n * - Monorepos (packages/*, apps/*)\n * - Various naming conventions (helpers, shared, core, etc.)\n */\n\nimport { readdirSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport { exists } from './fs.js';\n\n/**\n * Architecture layer definitions with alternative names.\n * Each layer maps to equivalent directory names.\n * Order defines hierarchy: earlier = lower layer.\n */\nconst ARCHITECTURE_LAYERS = [\n // Layer 0: Pure types (no imports)\n { layer: 'types', dirs: ['types', 'interfaces', 'schemas'] },\n // Layer 1: Utilities (only types)\n { layer: 'utils', dirs: ['utils', 'helpers', 'shared', 'common', 'core'] },\n // Layer 2: Libraries (types, utils)\n { layer: 'lib', dirs: ['lib', 'libraries'] },\n // Layer 3: State & logic (types, utils, lib)\n { layer: 'hooks', dirs: ['hooks', 'composables'] },\n { layer: 'services', dirs: ['services', 'api', 'stores', 'state'] },\n // Layer 4: UI components (all above)\n { layer: 'components', dirs: ['components', 'ui'] },\n // Layer 5: Features (all above)\n { layer: 'features', dirs: ['features', 'modules', 'domains'] },\n // Layer 6: Entry points (can import everything)\n { layer: 'app', dirs: ['app', 'pages', 'views', 'routes', 'commands'] },\n] as const;\n\ntype Layer = (typeof ARCHITECTURE_LAYERS)[number]['layer'];\n\n/**\n * Hierarchy rules: what each layer can import\n * Lower layers have fewer import permissions\n */\nconst HIERARCHY: Record<Layer, Layer[]> = {\n types: [],\n utils: ['types'],\n lib: ['utils', 'types'],\n hooks: ['lib', 'utils', 'types'],\n services: ['lib', 'utils', 'types'],\n components: ['hooks', 'services', 'lib', 'utils', 'types'],\n features: ['components', 'hooks', 'services', 'lib', 'utils', 'types'],\n app: ['features', 'components', 'hooks', 'services', 'lib', 'utils', 'types'],\n};\n\ninterface DetectedElement {\n layer: Layer;\n pattern: string; // glob pattern for boundaries config\n location: string; // human-readable location\n}\n\nexport interface DetectedArchitecture {\n elements: DetectedElement[];\n isMonorepo: boolean;\n}\n\n/**\n * Find monorepo package directories\n * @param projectDirectory\n */\nfunction findMonorepoPackages(projectDirectory: string): string[] {\n const packages: string[] = [];\n\n // Check common monorepo patterns\n const monorepoRoots = ['packages', 'apps', 'libs', 'modules'];\n\n for (const root of monorepoRoots) {\n const rootPath = nodePath.join(projectDirectory, root);\n if (!exists(rootPath)) continue;\n\n try {\n const entries = readdirSync(rootPath, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('.')) {\n packages.push(nodePath.join(root, entry.name));\n }\n }\n } catch {\n // Directory not readable, skip\n }\n }\n\n return packages;\n}\n\n/**\n * Check if a layer already exists for this path prefix\n * @param elements\n * @param layer\n * @param pathPrefix\n */\nfunction hasLayerForPrefix(elements: DetectedElement[], layer: Layer, pathPrefix: string): boolean {\n return elements.some(\n element => element.layer === layer && element.pattern.startsWith(pathPrefix),\n );\n}\n\n/**\n * Scan a single search path for architecture layers\n * @param projectDirectory\n * @param searchPath\n * @param pathPrefix\n * @param elements\n */\nfunction scanSearchPath(\n projectDirectory: string,\n searchPath: string,\n pathPrefix: string,\n elements: DetectedElement[],\n): void {\n for (const layerDefinition of ARCHITECTURE_LAYERS) {\n for (const dirName of layerDefinition.dirs) {\n const fullPath = nodePath.join(projectDirectory, searchPath, dirName);\n if (exists(fullPath) && !hasLayerForPrefix(elements, layerDefinition.layer, pathPrefix)) {\n elements.push({\n layer: layerDefinition.layer,\n pattern: `${pathPrefix}${dirName}/**`,\n location: `${pathPrefix}${dirName}`,\n });\n }\n }\n }\n}\n\n/**\n * Scan a directory for architecture layers\n * @param projectDirectory\n * @param basePath\n */\nfunction scanForLayers(projectDirectory: string, basePath: string): DetectedElement[] {\n const elements: DetectedElement[] = [];\n const prefix = basePath ? `${basePath}/` : '';\n\n // Check src/ and root level\n scanSearchPath(projectDirectory, nodePath.join(basePath, 'src'), `${prefix}src/`, elements);\n scanSearchPath(projectDirectory, basePath, prefix, elements);\n\n return elements;\n}\n\n/**\n * Detects architecture directories in the project\n * Handles both standard projects and monorepos\n * @param projectDirectory\n */\nexport function detectArchitecture(projectDirectory: string): DetectedArchitecture {\n const elements: DetectedElement[] = [];\n\n // First, check for monorepo packages\n const packages = findMonorepoPackages(projectDirectory);\n const isMonorepo = packages.length > 0;\n\n if (isMonorepo) {\n // Scan each package\n for (const pkg of packages) {\n elements.push(...scanForLayers(projectDirectory, pkg));\n }\n }\n\n // Also scan root level (works for both monorepo root and standard projects)\n elements.push(...scanForLayers(projectDirectory, ''));\n\n // Deduplicate by pattern\n const seen = new Set<string>();\n const uniqueElements = elements.filter(element => {\n if (seen.has(element.pattern)) return false;\n seen.add(element.pattern);\n return true;\n });\n\n return { elements: uniqueElements, isMonorepo };\n}\n\n/**\n * Format a single element for the config\n * @param el\n */\nfunction formatElement(element: DetectedElement): string {\n return ` { type: '${element.layer}', pattern: '${element.pattern}', mode: 'full' }`;\n}\n\n/**\n * Format allowed imports for a rule\n * @param allowed\n */\nfunction formatAllowedImports(allowed: Layer[]): string {\n return allowed.map(d => `'${d}'`).join(', ');\n}\n\n/**\n * Generate a single rule for what a layer can import\n * @param layer\n * @param detectedLayers\n */\nfunction generateRule(layer: Layer, detectedLayers: Set<Layer>): string | undefined {\n const allowedLayers = HIERARCHY[layer];\n if (allowedLayers.length === 0) return undefined;\n\n const allowed = allowedLayers.filter(dep => detectedLayers.has(dep));\n if (allowed.length === 0) return undefined;\n\n return ` { from: ['${layer}'], allow: [${formatAllowedImports(allowed)}] }`;\n}\n\n/**\n * Build description of what was detected\n * @param arch\n */\nfunction buildDetectedInfo(arch: DetectedArchitecture): string {\n if (arch.elements.length === 0) {\n return 'No architecture directories detected yet - add types/, utils/, components/, etc.';\n }\n const locations = arch.elements.map(element => element.location).join(', ');\n const monorepoNote = arch.isMonorepo ? ' (monorepo)' : '';\n return `Detected: ${locations}${monorepoNote}`;\n}\n\n/**\n *\n * @param arch\n */\nexport function generateBoundariesConfig(arch: DetectedArchitecture): string {\n const hasElements = arch.elements.length > 0;\n\n // Generate element definitions\n const elementsContent = arch.elements.map(element => formatElement(element)).join(',\\n');\n\n // Generate rules (what each layer can import)\n const detectedLayers = new Set(arch.elements.map(element => element.layer));\n const rules = [...detectedLayers]\n .map(layer => generateRule(layer, detectedLayers))\n .filter((rule): rule is string => rule !== undefined);\n const rulesContent = rules.join(',\\n');\n\n const detectedInfo = buildDetectedInfo(arch);\n\n return `/**\n * Architecture Boundaries Configuration (AUTO-GENERATED)\n *\n * ${detectedInfo}\n *\n * This enforces import boundaries between architectural layers:\n * - Lower layers (types, utils) cannot import from higher layers (components, features)\n * - Uses 'error' severity - LLMs ignore warnings, errors force compliance\n *\n * Recognized directories (in hierarchy order):\n * types → utils → lib → hooks/services → components → features/modules → app\n *\n * To customize, override in your eslint.config.mjs:\n * rules: { 'boundaries/element-types': ['error', { ... }] }\n */\n\nimport boundaries from 'eslint-plugin-boundaries';\n\nexport default {\n plugins: { boundaries },\n settings: {\n 'boundaries/elements': [\n${elementsContent}\n ],\n },\n rules: {${\n hasElements\n ? `\n 'boundaries/element-types': ['error', {\n default: 'disallow',\n rules: [\n${rulesContent}\n ],\n }],`\n : ''\n }\n 'boundaries/no-unknown': 'off', // Allow files outside defined elements\n 'boundaries/no-unknown-files': 'off', // Allow non-matching files\n },\n};\n`;\n}\n","/**\n * Dependency-cruiser config generator\n *\n * Generates dependency-cruiser configuration from detected architecture.\n * Used by `safeword sync-config` command and `/audit` slash command.\n */\n\nimport nodePath from 'node:path';\n\nimport type { DetectedArchitecture } from './boundaries.js';\nimport { readJson } from './fs.js';\n\nexport interface DepCruiseArchitecture extends DetectedArchitecture {\n workspaces?: string[];\n}\n\ninterface PackageJson {\n workspaces?: string[] | { packages?: string[] };\n}\n\n/**\n * Detect workspaces from package.json\n * Supports both array format and object format (yarn workspaces)\n */\nexport function detectWorkspaces(cwd: string): string[] | undefined {\n const packageJsonPath = nodePath.join(cwd, 'package.json');\n const packageJson = readJson(packageJsonPath) as PackageJson | undefined;\n\n if (!packageJson?.workspaces) return undefined;\n\n // Handle both formats: string[] or { packages: string[] }\n const workspaces = Array.isArray(packageJson.workspaces)\n ? packageJson.workspaces\n : packageJson.workspaces.packages;\n\n return workspaces && workspaces.length > 0 ? workspaces : undefined;\n}\n\n/**\n * Generate monorepo hierarchy rules based on workspace patterns\n */\nfunction generateMonorepoRules(workspaces: string[]): string {\n const rules: string[] = [];\n\n const hasLibs = workspaces.some(w => w.startsWith('libs'));\n const hasPackages = workspaces.some(w => w.startsWith('packages'));\n const hasApps = workspaces.some(w => w.startsWith('apps'));\n\n // libs cannot import packages or apps\n if (hasLibs && (hasPackages || hasApps)) {\n rules.push(` {\n name: 'libs-cannot-import-packages-or-apps',\n severity: 'error',\n from: { path: '^libs/' },\n to: { path: '^(packages|apps)/' },\n }`);\n }\n\n // packages cannot import apps\n if (hasPackages && hasApps) {\n rules.push(` {\n name: 'packages-cannot-import-apps',\n severity: 'error',\n from: { path: '^packages/' },\n to: { path: '^apps/' },\n }`);\n }\n\n return rules.join(',\\n');\n}\n\n/**\n * Generate .safeword/depcruise-config.cjs content (forbidden rules + options)\n */\nexport function generateDepCruiseConfigFile(arch: DepCruiseArchitecture): string {\n const monorepoRules = arch.workspaces ? generateMonorepoRules(arch.workspaces) : '';\n const hasMonorepoRules = monorepoRules.length > 0;\n\n return String.raw`module.exports = {\n forbidden: [\n // =========================================================================\n // ERROR RULES (block on violations)\n // =========================================================================\n {\n name: 'no-circular',\n comment: 'Circular dependencies cause runtime issues and make code hard to reason about',\n severity: 'error',\n from: {},\n to: { circular: true },\n },\n {\n name: 'no-deprecated-deps',\n comment: 'Deprecated npm packages should be replaced - they may have security issues or be unmaintained',\n severity: 'error',\n from: {},\n to: { dependencyTypes: ['deprecated'] },\n },${hasMonorepoRules ? `\\n${monorepoRules},` : ''}\n\n // =========================================================================\n // WARNING RULES (flag issues but don't block)\n // =========================================================================\n {\n name: 'no-dev-deps-in-src',\n comment: 'Production code should not import devDependencies - may cause runtime failures',\n severity: 'warn',\n from: {\n path: ['^src', '^packages/[^/]+/src'],\n pathNot: '\\\\.test\\\\.[tj]sx?$',\n },\n to: { dependencyTypes: ['npm-dev'] },\n },\n {\n name: 'no-orphans',\n comment: 'Orphan modules are not imported anywhere - may be dead code',\n severity: 'warn',\n from: {\n orphan: true,\n pathNot: [\n // Entry points\n '(^|/)index\\\\.[tj]sx?$',\n '(^|/)main\\\\.[tj]sx?$',\n '(^|/)cli\\\\.[tj]s$',\n '\\\\.config\\\\.[tj]s$',\n '\\\\.config\\\\.mjs$',\n // Test files\n '\\\\.test\\\\.[tj]sx?$',\n '\\\\.spec\\\\.[tj]sx?$',\n '/tests/',\n '/__tests__/',\n // Astro/Next.js pages and content\n '/src/content/',\n '/src/pages/',\n '/app/',\n ],\n },\n to: {},\n },\n ],\n options: {\n doNotFollow: { path: ['node_modules', '.safeword'] },\n exclude: {\n path: ['node_modules', 'dist', 'build', 'coverage', '\\\\.d\\\\.ts$'],\n },\n tsPreCompilationDeps: true,\n tsConfig: { fileName: 'tsconfig.json' },\n enhancedResolveOptions: {\n extensions: ['.ts', '.tsx', '.js', '.jsx'],\n exportsFields: ['exports'],\n conditionNames: ['import', 'require', 'node', 'default'],\n },\n },\n};\n`;\n}\n\n/**\n * Generate .dependency-cruiser.js (main config that imports generated)\n */\nexport function generateDepCruiseMainConfig(): string {\n return `/**\n * Dependency Cruiser Configuration\n *\n * Imports auto-generated rules from .safeword/depcruise-config.cjs\n * ADD YOUR CUSTOM RULES BELOW the spread operator.\n */\n\nconst generated = require('./.safeword/depcruise-config.cjs');\n\nmodule.exports = {\n forbidden: [\n ...generated.forbidden,\n // ADD YOUR CUSTOM RULES BELOW:\n // { name: 'no-legacy', from: { path: 'legacy/' }, to: { path: 'new/' } },\n ],\n options: {\n ...generated.options,\n // Your overrides here\n },\n};\n`;\n}\n"],"mappings":"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","names":["nodePath","nodePath","nodePath","nodePath"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  detect
3
- } from "./chunk-OYBKTOVF.js";
3
+ } from "./chunk-PMYWVDOF.js";
4
4
  import {
5
5
  VERSION
6
6
  } from "./chunk-ORQHKDT2.js";
@@ -794,9 +794,7 @@ var recommendedTypeScriptReact = [
794
794
  ];
795
795
 
796
796
  // src/presets/typescript/eslint-configs/recommended-nextjs.ts
797
- var recommendedTypeScriptNext = [
798
- // All React + TypeScript rules
799
- ...recommendedTypeScriptReact,
797
+ var nextOnlyRules = [
800
798
  // Next.js plugin with core-web-vitals config (stricter)
801
799
  nextPlugin.configs["core-web-vitals"],
802
800
  // Escalate ALL remaining warn rules to error (LLMs ignore warnings)
@@ -819,6 +817,37 @@ var recommendedTypeScriptNext = [
819
817
  }
820
818
  }
821
819
  ];
820
+ var recommendedTypeScriptNext = [
821
+ // All React + TypeScript rules
822
+ ...recommendedTypeScriptReact,
823
+ // Next.js-only rules
824
+ ...nextOnlyRules
825
+ ];
826
+
827
+ // src/presets/typescript/eslint-configs/storybook.ts
828
+ import storybookPlugin from "eslint-plugin-storybook";
829
+ var storybookConfig = [
830
+ // Use official flat/recommended as base (includes plugin setup + file patterns)
831
+ ...storybookPlugin.configs["flat/recommended"],
832
+ // Override warnings to errors + add strict rules
833
+ {
834
+ name: "safeword/storybook-strict",
835
+ files: ["**/*.stories.{ts,tsx,js,jsx,mjs,cjs}", "**/*.story.{ts,tsx,js,jsx,mjs,cjs}"],
836
+ rules: {
837
+ // Upgrade warn → error (LLMs ignore warnings)
838
+ "storybook/hierarchy-separator": "error",
839
+ "storybook/no-redundant-story-name": "error",
840
+ "storybook/prefer-pascal-case": "error",
841
+ // Strict rules not in recommended (useful for LLMs)
842
+ "storybook/csf-component": "error",
843
+ // component property should be set
844
+ "storybook/no-stories-of": "error",
845
+ // storiesOf is deprecated
846
+ "storybook/meta-inline-properties": "error"
847
+ // Meta should only have inline properties
848
+ }
849
+ }
850
+ ];
822
851
 
823
852
  // src/presets/typescript/eslint-configs/tailwind.ts
824
853
  import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
@@ -869,6 +898,10 @@ var tanstackQueryConfig = [
869
898
  }
870
899
  ];
871
900
 
901
+ // src/presets/typescript/eslint-configs/turbo.ts
902
+ import turboPlugin from "eslint-plugin-turbo";
903
+ var turboConfig = [turboPlugin.configs["flat/recommended"]];
904
+
872
905
  // src/presets/typescript/eslint-configs/vitest.ts
873
906
  import vitestPlugin from "eslint-plugin-vitest";
874
907
  var vitestConfig = [
@@ -925,11 +958,14 @@ var eslintPlugin = {
925
958
  recommendedTypeScript,
926
959
  recommendedTypeScriptReact,
927
960
  recommendedTypeScriptNext,
961
+ nextOnlyRules,
928
962
  astro: astroConfig,
929
963
  tailwind: tailwindConfig,
930
964
  tanstackQuery: tanstackQueryConfig,
931
965
  vitest: vitestConfig,
932
- playwright: playwrightConfig
966
+ playwright: playwrightConfig,
967
+ storybook: storybookConfig,
968
+ turbo: turboConfig
933
969
  },
934
970
  detect,
935
971
  rules,
@@ -945,11 +981,14 @@ export {
945
981
  recommended,
946
982
  recommendedTypeScript,
947
983
  recommendedTypeScriptReact,
984
+ nextOnlyRules,
948
985
  recommendedTypeScriptNext,
986
+ storybookConfig,
949
987
  tailwindConfig,
950
988
  tanstackQueryConfig,
989
+ turboConfig,
951
990
  vitestConfig,
952
991
  eslintPlugin,
953
992
  typescript_default
954
993
  };
955
- //# sourceMappingURL=chunk-NUKQPPCM.js.map
994
+ //# sourceMappingURL=chunk-NXSSFAFS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presets/typescript/eslint-configs/astro.ts","../src/presets/typescript/eslint-configs/base.ts","../src/presets/typescript/eslint-rules/no-accumulating-spread.ts","../src/presets/typescript/eslint-rules/no-incomplete-error-handling.ts","../src/presets/typescript/eslint-rules/no-re-export-all.ts","../src/presets/typescript/eslint-rules/index.ts","../src/presets/typescript/eslint-configs/playwright.ts","../src/presets/typescript/eslint-configs/recommended.ts","../src/presets/typescript/eslint-configs/recommended-nextjs.ts","../src/presets/typescript/eslint-configs/recommended-react.ts","../src/presets/typescript/eslint-configs/recommended-typescript.ts","../src/presets/typescript/eslint-configs/storybook.ts","../src/presets/typescript/eslint-configs/tailwind.ts","../src/presets/typescript/eslint-configs/tanstack-query.ts","../src/presets/typescript/eslint-configs/turbo.ts","../src/presets/typescript/eslint-configs/vitest.ts","../src/presets/typescript/index.ts"],"sourcesContent":["/**\n * ESLint configuration for Astro projects\n *\n * Applies to .astro files.\n * Includes recommended rules plus LLM-critical security/convention rules.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport astroPlugin from 'eslint-plugin-astro';\n\n/**\n * Astro config\n *\n * Includes:\n * - 8 recommended rules (all at error)\n * - 33 accessibility rules from jsx-a11y-strict (adapted for Astro)\n * - 3 LLM-critical rules: no-set-html-directive (XSS), no-unsafe-inline-scripts (CSP), no-exports-from-components\n *\n * Note: jsx-a11y rules work with Astro files because eslint-plugin-astro\n * provides wrapped versions that understand Astro's JSX-like syntax.\n * Using eslint-plugin-jsx-a11y directly on Astro files does NOT work.\n */\nexport const astroConfig: any[] = [\n // Spread flat/recommended (5 config objects: plugin setup, file patterns, prettier overrides, rules)\n ...astroPlugin.configs['flat/recommended'],\n\n // Accessibility rules adapted for Astro (requires eslint-plugin-jsx-a11y installed)\n ...astroPlugin.configs['flat/jsx-a11y-strict'],\n\n // Add LLM-critical rules\n {\n name: 'safeword/astro',\n rules: {\n // XSS prevention - LLMs often use set:html for rendering user content\n 'astro/no-set-html-directive': 'error',\n\n // CSP safety - inline scripts can break Content Security Policy\n 'astro/no-unsafe-inline-scripts': 'error',\n\n // Astro convention - LLMs try to export from .astro components (not allowed)\n 'astro/no-exports-from-components': 'error',\n },\n },\n];\n","/**\n * Base ESLint plugins shared between JS and TypeScript configs\n *\n * These plugins work without type information and are included in both\n * `recommended` (JS) and `recommendedTypeScript` configs.\n */\n\nimport js from '@eslint/js';\nimport { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript';\nimport { importX } from 'eslint-plugin-import-x';\nimport pluginPromise from 'eslint-plugin-promise';\nimport { configs as regexpConfigs } from 'eslint-plugin-regexp';\nimport pluginSecurity from 'eslint-plugin-security';\nimport simpleImportSort from 'eslint-plugin-simple-import-sort';\nimport { configs as sonarConfigs } from 'eslint-plugin-sonarjs';\nimport unicorn from 'eslint-plugin-unicorn';\n\nimport { rules as safewordRules } from '../eslint-rules/index.js';\n\n/**\n * File patterns for base JS/TS rules\n * Excludes .astro, .vue, .svelte which use different parsers\n */\nexport const JS_TS_FILES = ['**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}'];\n\n/**\n * Add files restriction to config objects.\n * Handles both single config objects and arrays of configs.\n * Skips config objects that only have ignores (global ignores).\n * Skips config objects that already have files set.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages\nfunction scopeConfigToFiles(config: any, files: string[]): any {\n // Skip global ignores (config with only ignores property)\n if (config.ignores && Object.keys(config).length === 1) {\n return config;\n }\n // Skip configs that already have files set\n if (config.files) {\n return config;\n }\n // Add files restriction\n return { ...config, files };\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages\nfunction scopeToFiles(configs: any[], files: string[]): any[] {\n return configs.flatMap(config => {\n // Handle arrays (third-party configs may be arrays)\n if (Array.isArray(config)) {\n return config.map(c => scopeConfigToFiles(c, files));\n }\n return scopeConfigToFiles(config, files);\n });\n}\n\n/**\n * Base plugins - shared between JS and TS configs\n * Does NOT include JSDoc (different config per language) or Prettier (must be last)\n *\n * Note: Uses any[] because ESLint plugin types are incompatible across packages.\n * Runtime validation by ESLint ensures correctness.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages\nconst basePluginsUnscoped: any[] = [\n // Default ignores - always skip these directories\n {\n ignores: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**'],\n },\n\n // ESLint core recommended\n js.configs.recommended,\n\n // Code style and design rules - catches common LLM patterns\n {\n name: 'safeword/code-style',\n rules: {\n 'no-unneeded-ternary': 'error', // x ? true : false → x\n 'prefer-template': 'error', // 'a' + b → `a${b}`\n 'dot-notation': 'error', // obj[\"prop\"] → obj.prop\n 'object-shorthand': 'error', // { foo: foo } → { foo }\n 'no-extra-boolean-cast': 'error', // !!value → Boolean(value) or value\n 'prefer-object-spread': 'error', // Object.assign({}, x) → { ...x }\n 'logical-assignment-operators': 'error', // x = x ?? y → x ??= y\n 'operator-assignment': 'error', // x = x + 1 → x += 1\n curly: 'error', // Require braces around if/else/for/while\n 'arrow-body-style': ['error', 'as-needed'], // () => { return x } → () => x\n 'prefer-arrow-callback': ['error', { allowNamedFunctions: true }], // function() {} → () => {}\n // Design constraints - forces LLMs to decompose code\n 'max-depth': ['error', 4], // Forces early returns, avoids deep nesting\n 'max-params': ['error', 5], // Forces object params or decomposition\n complexity: ['error', 10], // Cyclomatic complexity - LLMs write dense, complex code\n 'max-nested-callbacks': ['error', 3], // Prevents callback hell in async code\n eqeqeq: ['error', 'always', { null: 'ignore' }], // === required, except x == null\n 'preserve-caught-error': 'error', // Re-throw with { cause: error } to preserve stack\n },\n },\n\n // Import validation\n importX.flatConfigs.recommended,\n {\n name: 'safeword/import-rules',\n settings: {\n 'import-x/resolver-next': [createTypeScriptImportResolver()],\n },\n rules: {\n 'import-x/no-duplicates': 'error', // LLMs create duplicate imports\n 'import-x/no-cycle': 'error', // Circular dependencies A → B → A\n 'import-x/no-self-import': 'error', // File imports itself (copy-paste bug)\n // Turn off rules with high false-positive rate - documented justification:\n //\n // no-named-as-default: Flags valid patterns like `import Button from './Button'`\n // when Button.tsx has both default and named exports. Very common in React.\n // Issue: https://github.com/import-js/eslint-plugin-import/issues/1618\n 'import-x/no-named-as-default': 'off',\n //\n // no-named-as-default-member: Same issue - flags accessing static members\n // on default imports, e.g., `Button.displayName`. Common React pattern.\n 'import-x/no-named-as-default-member': 'off',\n },\n },\n\n // Code quality / complexity\n sonarConfigs.recommended,\n {\n name: 'safeword/sonarjs-rules',\n rules: {\n // Enable design rules (off by default but valuable for clean code)\n 'sonarjs/no-collapsible-if': 'error', // if(a) { if(b) } → if(a && b)\n 'sonarjs/no-nested-switch': 'error', // Switch inside switch is a smell\n 'sonarjs/prefer-immediate-return': 'error', // const x = y; return x → return y\n 'sonarjs/no-inconsistent-returns': 'error', // Some paths return, some don't\n },\n },\n\n // Security - detect common vulnerabilities\n pluginSecurity.configs.recommended,\n {\n name: 'safeword/security-rules',\n rules: {\n // Critical security rules at error (LLMs ignore warnings)\n 'security/detect-bidi-characters': 'error', // Trojan Source attacks\n 'security/detect-eval-with-expression': 'error',\n 'security/detect-non-literal-fs-filename': 'error',\n 'security/detect-non-literal-regexp': 'error',\n 'security/detect-non-literal-require': 'error',\n 'security/detect-child-process': 'error',\n 'security/detect-unsafe-regex': 'error',\n 'security/detect-disable-mustache-escape': 'error',\n 'security/detect-no-csrf-before-method-override': 'error',\n // Escalate all to error (LLMs ignore warnings)\n 'security/detect-object-injection': 'error',\n 'security/detect-possible-timing-attacks': 'error',\n 'security/detect-buffer-noassert': 'error',\n 'security/detect-new-buffer': 'error',\n 'security/detect-pseudoRandomBytes': 'error',\n },\n },\n\n // Promise handling - catches floating promises (critical for LLM code)\n pluginPromise.configs['flat/recommended'],\n {\n name: 'safeword/promise-rules',\n rules: {\n 'promise/no-multiple-resolved': 'error', // Catches missing return after resolve\n // LLMs mix callback/promise paradigms - escalate to error\n 'promise/no-callback-in-promise': 'error',\n 'promise/no-nesting': 'error',\n 'promise/no-promise-in-callback': 'error',\n 'promise/no-return-in-finally': 'error',\n 'promise/valid-params': 'error',\n },\n },\n\n // Regexp - catches ReDoS vulnerabilities and malformed regex\n regexpConfigs['flat/recommended'],\n {\n name: 'safeword/regexp-rules',\n rules: {\n // Escalate warn rules to error (LLMs ignore warnings)\n 'regexp/confusing-quantifier': 'error',\n 'regexp/no-empty-alternative': 'error',\n 'regexp/no-lazy-ends': 'error',\n 'regexp/no-potentially-useless-backreference': 'error',\n 'regexp/no-useless-flag': 'error',\n 'regexp/optimal-lookaround-quantifier': 'error',\n },\n },\n\n // Modern JS enforcement - strict for agents\n unicorn.configs.recommended,\n {\n name: 'safeword/unicorn-rules',\n rules: {\n // Keep off - documented justification for each:\n //\n // no-process-exit: CLI tools legitimately use process.exit() for:\n // - Error handling with non-zero exit codes\n // - Clean shutdown after completing work\n // Forcing throw-only would break CLI UX and exit code contracts.\n 'unicorn/no-process-exit': 'off',\n //\n // prefer-module: CommonJS is still valid in Node.js ecosystem:\n // - Config files (jest.config.js, .eslintrc.cjs)\n // - Packages with CJS-only dependencies\n // - Gradual ESM migration in progress\n 'unicorn/prefer-module': 'off',\n // Escalated to error for LLM code\n 'unicorn/switch-case-braces': 'error',\n 'unicorn/catch-error-name': 'error',\n 'unicorn/no-array-reduce': 'error', // LLMs write confusing reduce\n 'unicorn/prevent-abbreviations': [\n 'error',\n {\n allowList: {\n ctx: true, // context\n req: true, // request\n res: true, // response\n err: true, // error\n dir: true, // directory\n pkg: true, // package\n env: true, // environment\n args: true, // arguments\n params: true, // parameters\n props: true, // properties\n ref: true, // reference\n src: true, // source\n dest: true, // destination\n db: true, // database\n fn: true, // function\n cb: true, // callback\n acc: true, // accumulator\n prev: true, // previous\n curr: true, // current\n i: true, // index\n j: true, // index\n k: true, // index\n },\n },\n ],\n 'unicorn/no-null': 'error', // Use undefined\n 'unicorn/no-array-for-each': 'error', // Use for...of\n 'unicorn/no-negated-condition': 'error', // Clearer conditionals\n // Cherry-picked from 'all' config - high value for LLM code\n 'unicorn/no-unused-properties': 'error', // Dead code in enum-like objects\n 'unicorn/consistent-destructuring': 'error', // Don't mix props.x and {x} = props\n 'unicorn/prefer-import-meta-properties': 'error', // import.meta.dirname > __dirname\n },\n },\n\n // Import sorting - auto-fixable, reduces noise\n {\n name: 'safeword/import-sort',\n plugins: { 'simple-import-sort': simpleImportSort },\n rules: {\n 'simple-import-sort/imports': 'error',\n 'simple-import-sort/exports': 'error',\n 'import-x/order': 'off', // Disable in favor of simple-import-sort\n },\n },\n\n // Safeword custom rules - LLM-specific patterns\n {\n name: 'safeword/custom-rules',\n plugins: { safeword: { rules: safewordRules } },\n rules: {\n 'safeword/no-incomplete-error-handling': 'error',\n 'safeword/no-accumulating-spread': 'error', // O(n²) reduce pattern\n 'safeword/no-re-export-all': 'error', // Hurts tree-shaking\n },\n },\n];\n\n/**\n * Base plugins scoped to JS/TS files only.\n * Prevents rules from running on .astro, .vue, .svelte files which use different parsers.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages\nexport const basePlugins: any[] = scopeToFiles(basePluginsUnscoped, JS_TS_FILES);\n\n/**\n * Prettier config - must be last to disable conflicting rules\n */\n\nexport { default as prettierConfig } from 'eslint-config-prettier';\n","/**\n * Rule: no-accumulating-spread\n *\n * Detects spread operator on accumulators in reduce callbacks, which causes\n * O(n²) time complexity. This is a common LLM performance mistake.\n *\n * Bad (O(n²)):\n * items.reduce((acc, item) => ({ ...acc, [item.id]: item }), {})\n * items.reduce((acc, item) => [...acc, item.name], [])\n *\n * Good (O(n)):\n * items.reduce((acc, item) => { acc[item.id] = item; return acc; }, {})\n * items.reduce((acc, item) => { acc.push(item.name); return acc; }, [])\n *\n * Or better - avoid reduce entirely:\n * Object.fromEntries(items.map(item => [item.id, item]))\n * items.map(item => item.name)\n */\n\nimport type { Rule } from 'eslint';\nimport type {\n ArrowFunctionExpression,\n CallExpression,\n FunctionExpression,\n Node,\n SpreadElement,\n} from 'estree';\n\n/**\n * Check if a node is a call to .reduce()\n */\nfunction isReduceCall(node: CallExpression): boolean {\n const { callee } = node;\n return (\n callee.type === 'MemberExpression' &&\n callee.property.type === 'Identifier' &&\n callee.property.name === 'reduce'\n );\n}\n\n/**\n * Get the accumulator parameter name from a reduce callback\n */\nfunction getAccumulatorName(\n callback: ArrowFunctionExpression | FunctionExpression,\n): string | undefined {\n const firstParameter = callback.params[0];\n if (firstParameter?.type === 'Identifier') {\n return firstParameter.name;\n }\n return undefined;\n}\n\n/**\n * Check if a spread element spreads the accumulator\n */\nfunction spreadsAccumulator(spread: SpreadElement, accName: string): boolean {\n return spread.argument.type === 'Identifier' && spread.argument.name === accName;\n}\n\n/**\n * Check object expression properties for accumulator spread.\n */\nfunction findSpreadInObject(\n node: Node & { type: 'ObjectExpression' },\n accName: string,\n): SpreadElement | undefined {\n for (const property of node.properties) {\n if (property.type === 'SpreadElement' && spreadsAccumulator(property, accName)) {\n return property;\n }\n }\n return undefined;\n}\n\n/**\n * Check array expression elements for accumulator spread.\n */\nfunction findSpreadInArray(\n node: Node & { type: 'ArrayExpression' },\n accName: string,\n): SpreadElement | undefined {\n for (const element of node.elements) {\n if (element?.type === 'SpreadElement' && spreadsAccumulator(element, accName)) {\n return element;\n }\n }\n return undefined;\n}\n\n/**\n * Recursively check if an expression contains a spread of the accumulator\n */\nfunction containsAccumulatorSpread(node: Node, accName: string): SpreadElement | undefined {\n // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- Only specific node types can contain spreads; all others return undefined via default\n switch (node.type) {\n case 'SpreadElement': {\n return spreadsAccumulator(node, accName) ? node : undefined;\n }\n\n case 'ObjectExpression': {\n return findSpreadInObject(node, accName);\n }\n\n case 'ArrayExpression': {\n return findSpreadInArray(node, accName);\n }\n\n case 'ConditionalExpression': {\n return (\n containsAccumulatorSpread(node.consequent, accName) ??\n containsAccumulatorSpread(node.alternate, accName)\n );\n }\n\n case 'LogicalExpression': {\n return (\n containsAccumulatorSpread(node.left, accName) ??\n containsAccumulatorSpread(node.right, accName)\n );\n }\n\n default: {\n return undefined;\n }\n }\n}\n\n/**\n * Check arrow function body for accumulator spread\n */\nfunction checkArrowBody(\n body: ArrowFunctionExpression['body'],\n accName: string,\n): SpreadElement | undefined {\n // Direct return: (acc, item) => ({ ...acc, ... })\n if (body.type === 'ObjectExpression' || body.type === 'ArrayExpression') {\n return containsAccumulatorSpread(body, accName);\n }\n\n // Parenthesized or conditional\n if (body.type === 'ConditionalExpression' || body.type === 'LogicalExpression') {\n return containsAccumulatorSpread(body, accName);\n }\n\n // Block body - check return statements\n if (body.type === 'BlockStatement') {\n for (const stmt of body.body) {\n if (stmt.type === 'ReturnStatement' && stmt.argument) {\n const found = containsAccumulatorSpread(stmt.argument, accName);\n if (found) return found;\n }\n }\n }\n\n return undefined;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow spreading accumulator in reduce (causes O(n²) complexity)',\n recommended: true,\n },\n messages: {\n accumulatingSpread:\n 'Spreading accumulator in reduce() causes O(n²) complexity. ' +\n 'Mutate the accumulator instead, or use map/filter/Object.fromEntries.',\n },\n schema: [],\n },\n\n create(context) {\n return {\n CallExpression(node: CallExpression) {\n if (!isReduceCall(node)) return;\n\n const callback = node.arguments[0];\n if (\n !callback ||\n (callback.type !== 'ArrowFunctionExpression' && callback.type !== 'FunctionExpression')\n ) {\n return;\n }\n\n const accName = getAccumulatorName(callback);\n if (!accName) return;\n\n const spreadNode = checkArrowBody(callback.body, accName);\n if (spreadNode) {\n context.report({\n node: spreadNode,\n messageId: 'accumulatingSpread',\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Rule: no-incomplete-error-handling\n *\n * Detects catch blocks that log an error but don't rethrow or return,\n * which swallows the error silently. This is a common LLM mistake.\n *\n * Bad:\n * catch (error) { console.error(error); } // swallowed!\n *\n * Good:\n * catch (error) { console.error(error); throw error; }\n * catch (error) { console.error(error); return null; }\n * catch (error) { throw new AppError('context', { cause: error }); }\n */\n\nimport type { Rule } from 'eslint';\nimport type { CallExpression, CatchClause, Statement } from 'estree';\n\nconst LOG_METHODS = new Set(['log', 'error', 'warn', 'info', 'debug', 'trace']);\n\nconst LOG_OBJECTS = new Set(['console', 'logger', 'log']);\n\n/**\n * Checks if a call expression is a logging call (console.log, logger.error, etc.)\n * @param node\n */\nfunction isLoggingCall(node: CallExpression): boolean {\n const { callee } = node;\n\n // console.error(...), logger.error(...), etc.\n if (\n callee.type === 'MemberExpression' &&\n callee.object.type === 'Identifier' &&\n callee.property.type === 'Identifier'\n ) {\n const object = callee.object.name.toLowerCase();\n const method = callee.property.name.toLowerCase();\n return LOG_OBJECTS.has(object) && LOG_METHODS.has(method);\n }\n\n return false;\n}\n\n/**\n * Check if a single statement terminates control flow.\n */\nfunction isTerminatingBranch(stmt: Statement): boolean {\n if (stmt.type === 'ThrowStatement' || stmt.type === 'ReturnStatement') {\n return true;\n }\n if (stmt.type === 'BlockStatement') {\n return hasTerminatingStatement(stmt.body);\n }\n return false;\n}\n\n/**\n * Check if an if statement terminates (both branches must terminate).\n */\nfunction ifStatementTerminates(stmt: Statement & { type: 'IfStatement' }): boolean {\n const consequentTerminates = isTerminatingBranch(stmt.consequent);\n const alternateTerminates = stmt.alternate ? isTerminatingBranch(stmt.alternate) : false;\n return consequentTerminates && alternateTerminates;\n}\n\n/**\n * Checks if statements include a throw or return (error is properly handled)\n * @param statements\n */\nfunction hasTerminatingStatement(statements: Statement[]): boolean {\n for (const stmt of statements) {\n if (stmt.type === 'ThrowStatement' || stmt.type === 'ReturnStatement') {\n return true;\n }\n if (stmt.type === 'IfStatement' && ifStatementTerminates(stmt)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a single statement is a logging call.\n */\nfunction isLoggingStatement(stmt: Statement): boolean {\n return (\n stmt.type === 'ExpressionStatement' &&\n stmt.expression.type === 'CallExpression' &&\n isLoggingCall(stmt.expression)\n );\n}\n\n/**\n * Get nested statements from a statement (for recursive search).\n */\nfunction getNestedStatements(stmt: Statement): Statement[] {\n if (stmt.type === 'BlockStatement') {\n return stmt.body;\n }\n if (stmt.type === 'IfStatement') {\n const nested = [stmt.consequent];\n if (stmt.alternate) nested.push(stmt.alternate);\n return nested;\n }\n return [];\n}\n\n/**\n * Recursively checks if statements include a logging call (searches nested blocks)\n * @param statements\n */\nfunction containsLoggingCall(statements: Statement[]): boolean {\n for (const stmt of statements) {\n if (isLoggingStatement(stmt)) return true;\n\n const nested = getNestedStatements(stmt);\n if (nested.length > 0 && containsLoggingCall(nested)) return true;\n }\n return false;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow catch blocks that log but do not rethrow or return',\n recommended: true,\n },\n messages: {\n incompleteErrorHandling:\n 'Catch block logs error but does not rethrow or return. This swallows the error silently.',\n },\n schema: [],\n },\n\n create(context) {\n return {\n CatchClause(node: CatchClause) {\n const { body } = node;\n if (body.type !== 'BlockStatement') return;\n\n const statements = body.body;\n\n // Only flag if there's a logging call but no terminating statement\n if (containsLoggingCall(statements) && !hasTerminatingStatement(statements)) {\n context.report({\n node,\n messageId: 'incompleteErrorHandling',\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Rule: no-re-export-all\n *\n * Disallows `export * from` statements which re-export all exports from\n * another module. This pattern hurts tree-shaking and makes dependencies\n * unclear. LLMs often use this for convenience without understanding\n * the bundle size implications.\n *\n * Bad:\n * export * from './utils';\n * export * from '@/components';\n *\n * Good:\n * export { foo, bar } from './utils';\n * export { Button, Modal } from '@/components';\n *\n * Note: `export * as namespace from` is allowed as it creates a named export.\n */\n\nimport type { Rule } from 'eslint';\nimport type { ExportAllDeclaration } from 'estree';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Disallow wildcard re-exports (export * from)',\n recommended: true,\n },\n messages: {\n noReExportAll:\n 'Avoid `export * from`. Use named exports for better tree-shaking and clearer dependencies.',\n },\n schema: [],\n },\n\n create(context) {\n return {\n ExportAllDeclaration(node: ExportAllDeclaration) {\n // Allow `export * as namespace from` - this creates a named export\n if (node.exported) {\n return;\n }\n\n context.report({\n node,\n messageId: 'noReExportAll',\n });\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Safeword custom ESLint rules\n */\n\nimport noAccumulatingSpread from './no-accumulating-spread.js';\nimport noIncompleteErrorHandling from './no-incomplete-error-handling.js';\nimport noReExportAll from './no-re-export-all.js';\n\nexport const rules = {\n 'no-accumulating-spread': noAccumulatingSpread,\n 'no-incomplete-error-handling': noIncompleteErrorHandling,\n 'no-re-export-all': noReExportAll,\n};\n","/**\n * ESLint configuration for Playwright e2e tests\n *\n * Applies ONLY to e2e test files to avoid conflicts with vitest:\n * - *.e2e.ts files (explicit e2e naming)\n * - Files in e2e/ directories\n *\n * Does NOT apply to regular *.test.ts files (those are vitest).\n *\n * All rules escalated to error EXCEPT no-skipped-test (stays warn for TDD).\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport playwrightPlugin from 'eslint-plugin-playwright';\n\n/**\n * Playwright e2e test linting config\n *\n * Based on recommended config with all warns escalated to error.\n * Exception: no-skipped-test stays at warn (legitimate TDD pattern).\n *\n * File patterns target only e2e tests to avoid vitest conflicts:\n * - Explicit e2e suffix (e.g., login.e2e.ts)\n * - Test/spec files in e2e directories only\n */\nexport const playwrightConfig: any[] = [\n {\n name: 'safeword/playwright',\n files: ['**/*.e2e.{ts,tsx,js,jsx}', '**/e2e/**/*.{test,spec}.{ts,tsx,js,jsx}'],\n plugins: {\n playwright: playwrightPlugin,\n },\n rules: {\n // From recommended - already at error\n 'playwright/missing-playwright-await': 'error',\n 'playwright/no-focused-test': 'error',\n 'playwright/no-networkidle': 'error',\n 'playwright/no-standalone-expect': 'error',\n 'playwright/no-unsafe-references': 'error',\n 'playwright/no-unused-locators': 'error',\n 'playwright/no-wait-for-navigation': 'error',\n 'playwright/prefer-web-first-assertions': 'error',\n 'playwright/valid-describe-callback': 'error',\n 'playwright/valid-expect': 'error',\n 'playwright/valid-expect-in-promise': 'error',\n 'playwright/valid-test-tags': 'error',\n 'playwright/valid-title': 'error',\n\n // Escalated from warn to error (LLMs ignore warnings)\n 'playwright/expect-expect': 'error',\n 'playwright/max-nested-describe': 'error',\n 'playwright/no-conditional-expect': 'error',\n 'playwright/no-conditional-in-test': 'error',\n 'playwright/no-element-handle': 'error',\n 'playwright/no-eval': 'error',\n 'playwright/no-force-option': 'error',\n 'playwright/no-nested-step': 'error',\n 'playwright/no-page-pause': 'error',\n 'playwright/no-useless-await': 'error',\n 'playwright/no-useless-not': 'error',\n 'playwright/no-wait-for-selector': 'error',\n 'playwright/no-wait-for-timeout': 'error',\n\n // EXCEPTION: stays at warn (legitimate TDD pattern)\n 'playwright/no-skipped-test': 'warn',\n\n // Relax base rules for test files - each override has documented justification:\n //\n // no-empty-function: Tests often need empty callbacks for mocks/stubs:\n // const mockFn = vi.fn(() => {}); // Valid mock with no implementation\n // await expect(action).rejects.toThrow(); // Empty catch in expect wrapper\n '@typescript-eslint/no-empty-function': 'off',\n //\n // detect-non-literal-fs-filename: Tests read fixtures from known safe paths:\n // const fixture = readFileSync(join(__dirname, 'fixtures', testCase.input));\n // Test fixtures are developer-controlled, not user input.\n 'security/detect-non-literal-fs-filename': 'off',\n //\n // no-null: Playwright API explicitly uses null in signatures:\n // await page.waitForFunction(() => window.loaded, null, { timeout: 5000 });\n // See: https://playwright.dev/docs/api/class-page#page-wait-for-function\n 'unicorn/no-null': 'off',\n },\n },\n];\n","/**\n * Recommended ESLint configuration for JavaScript + LLM coding agents\n *\n * This preset bundles and configures multiple ESLint plugins with\n * severity levels optimized for catching common LLM-generated code issues.\n *\n * Philosophy: LLMs ignore warnings, so rules that catch real bugs are at \"error\".\n *\n * For TypeScript projects, use `recommendedTypeScript` instead.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport pluginJsdoc from 'eslint-plugin-jsdoc';\n\nimport { basePlugins, prettierConfig } from './base.js';\n\n/**\n * JavaScript recommended config - core plugins without TypeScript\n *\n * Note: Uses any[] because ESLint plugin types are incompatible across packages.\n * Runtime validation by ESLint ensures correctness.\n */\n\nexport const recommended: any[] = [\n // All base plugins (security, promise, unicorn, etc.)\n ...basePlugins,\n\n // JSDoc - JavaScript needs docs (no type safety net)\n // Using error config - LLMs ignore warnings\n pluginJsdoc.configs['flat/recommended-error'],\n\n // Prettier must be last to disable conflicting rules\n prettierConfig,\n\n // Re-enable curly after prettier (prettier turns it off but we want braces for LLM code)\n {\n rules: {\n curly: 'error', // Force braces on if/else/for/while - LLMs write unsafe single-line blocks\n },\n },\n];\n","/**\n * Recommended ESLint configuration for Next.js + TypeScript + LLM coding agents\n *\n * Extends the React config with Next.js-specific rules:\n * - @next/eslint-plugin-next: Framework rules (Image, Link, Head, etc.)\n *\n * Philosophy: LLMs make Next.js-specific mistakes. All rules at error severity.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport nextPlugin from '@next/eslint-plugin-next';\n\nimport { recommendedTypeScriptReact } from './recommended-react.js';\n\n/**\n * Next.js-only rules for monorepo scoping\n *\n * Contains ONLY Next.js-specific ESLint rules without React rules.\n * Use with `files:` scoping in monorepos where only some packages use Next.js,\n * while React rules apply to all React packages.\n */\nexport const nextOnlyRules: any[] = [\n // Next.js plugin with core-web-vitals config (stricter)\n nextPlugin.configs['core-web-vitals'],\n\n // Escalate ALL remaining warn rules to error (LLMs ignore warnings)\n {\n name: 'safeword/nextjs-rules',\n rules: {\n '@next/next/google-font-display': 'error',\n '@next/next/google-font-preconnect': 'error',\n '@next/next/next-script-for-ga': 'error',\n '@next/next/no-async-client-component': 'error',\n '@next/next/no-before-interactive-script-outside-document': 'error',\n '@next/next/no-css-tags': 'error',\n '@next/next/no-head-element': 'error',\n '@next/next/no-img-element': 'error',\n '@next/next/no-page-custom-font': 'error',\n '@next/next/no-styled-jsx-in-document': 'error',\n '@next/next/no-title-in-document-head': 'error',\n '@next/next/no-typos': 'error',\n '@next/next/no-unwanted-polyfillio': 'error',\n },\n },\n];\n\n/**\n * Next.js + TypeScript recommended config\n *\n * Extends React config with Next.js-specific rules for catching\n * common LLM mistakes: using <img> instead of <Image>, <a> instead of <Link>.\n */\nexport const recommendedTypeScriptNext: any[] = [\n // All React + TypeScript rules\n ...recommendedTypeScriptReact,\n\n // Next.js-only rules\n ...nextOnlyRules,\n];\n","/**\n * Recommended ESLint configuration for React + TypeScript + LLM coding agents\n *\n * Extends the TypeScript config with React-specific rules:\n * - eslint-plugin-react: JSX rules (keys, duplicates, etc.)\n * - eslint-plugin-react-hooks 7.x: Hook rules + React Compiler diagnostics\n * - eslint-plugin-jsx-a11y: Accessibility rules (strict preset)\n *\n * Philosophy: LLMs make React-specific mistakes. These rules catch them.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport jsxA11y from 'eslint-plugin-jsx-a11y';\nimport reactPlugin from 'eslint-plugin-react';\nimport reactHooksPluginImport from 'eslint-plugin-react-hooks';\n\n// Type assertion - react-hooks 7.x exports configs but types don't declare it\nconst reactHooksPlugin = reactHooksPluginImport as unknown as {\n configs?: { flat?: { 'recommended-latest'?: any } };\n};\n\nimport { recommendedTypeScript } from './recommended-typescript.js';\n\n// Runtime validation - ensure react-hooks 7.x with flat config support\nconst reactHooksConfig = reactHooksPlugin.configs?.flat?.['recommended-latest'];\nif (!reactHooksConfig) {\n throw new Error(\n 'safeword requires eslint-plugin-react-hooks >= 7.0.0 with flat config support. ' +\n 'Please upgrade react-hooks: npm install eslint-plugin-react-hooks@latest',\n );\n}\n\n/**\n * React + TypeScript recommended config\n *\n * Extends TypeScript config with React-specific rules for catching\n * common LLM mistakes: missing keys, hook violations, stale closures.\n *\n * Includes React Compiler rules (v7.x) for detecting purity violations,\n * improper memoization, and other compiler-incompatible patterns.\n */\nexport const recommendedTypeScriptReact: any[] = [\n // All TypeScript rules (includes base plugins)\n ...recommendedTypeScript,\n\n // React plugin - JSX rules\n reactPlugin.configs.flat?.recommended,\n reactPlugin.configs.flat?.['jsx-runtime'], // React 17+ (no import React needed)\n\n // React Hooks + Compiler rules (v7.x flat config)\n // Using recommended-latest which includes void-use-memo\n reactHooksConfig,\n\n // Accessibility rules - strict preset (all at error level)\n jsxA11y.flatConfigs.strict,\n\n // Escalate warn rules to error + add LLM-critical rules\n {\n name: 'safeword/react-hooks-rules',\n rules: {\n // Escalate default warns to error (LLMs ignore warnings)\n 'react-hooks/exhaustive-deps': 'error', // Default: warn\n 'react-hooks/incompatible-library': 'error', // Default: warn\n 'react-hooks/unsupported-syntax': 'error', // Default: warn\n\n // LLM-critical rules NOT in recommended-latest preset\n 'react-hooks/memoized-effect-dependencies': 'error', // LLMs create unstable refs as deps\n 'react-hooks/no-deriving-state-in-effects': 'error', // LLMs derive state in useEffect\n },\n },\n\n // React rule overrides for TypeScript projects\n {\n name: 'safeword/react-rules',\n rules: {\n // Turn off rules that are redundant with TypeScript\n 'react/prop-types': 'off', // TS handles prop validation\n 'react/react-in-jsx-scope': 'off', // Not needed with React 17+\n\n // Escalate important rules to error\n 'react/jsx-key': 'error', // LLMs forget keys in map()\n 'react/jsx-no-duplicate-props': 'error', // Copy-paste bugs\n 'react/no-direct-mutation-state': 'error', // Critical React bug\n 'react/no-children-prop': 'error', // Anti-pattern\n 'react/jsx-no-target-blank': 'error', // Security - has autofix\n 'react/no-unknown-property': 'error', // class -> className, has autofix\n 'react/no-unescaped-entities': 'error', // XSS prevention\n },\n },\n];\n","/**\n * Recommended ESLint configuration for TypeScript + LLM coding agents\n *\n * Extends the base recommended config with typescript-eslint's\n * strictTypeChecked + stylisticTypeChecked presets.\n *\n * Type-checked rules are critical for LLM code - they catch:\n * - Floating promises (forgot await)\n * - Misused promises (passing promise where value expected)\n * - Unsafe any usage\n * - Incorrect async/await patterns\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport { importX } from 'eslint-plugin-import-x';\nimport { configs as tseslintConfigs } from 'typescript-eslint';\n\nimport { basePlugins, prettierConfig } from './base.js';\n\n/**\n * File patterns for TypeScript files.\n * Used for parser options and type-checked rules.\n */\nconst TS_FILES = ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'];\n\n/**\n * TypeScript recommended config - all base plugins + typescript-eslint strict\n *\n * Requires: tsconfig.json in project root (or configured via languageOptions)\n *\n * Note: Uses any[] because ESLint plugin types are incompatible across packages.\n * Runtime validation by ESLint ensures correctness.\n */\n\nexport const recommendedTypeScript: any[] = [\n // All base plugins (security, promise, unicorn, etc.)\n ...basePlugins,\n\n // TypeScript-specific import config\n importX.flatConfigs.typescript,\n\n // typescript-eslint strict + stylistic (type-checked)\n ...tseslintConfigs.strictTypeChecked,\n ...tseslintConfigs.stylisticTypeChecked,\n\n // Enable projectService for type-checked rules (modern approach, auto-discovers tsconfig)\n {\n name: 'safeword/typescript-parser-options',\n files: TS_FILES,\n languageOptions: {\n parserOptions: {\n projectService: true,\n },\n },\n },\n\n // Disable type-checked rules for non-TS files (no type info available)\n // Includes JS files and .astro files (which use astro-eslint-parser)\n {\n ...tseslintConfigs.disableTypeChecked,\n name: 'safeword/disable-type-checked-for-non-ts',\n files: ['**/*.js', '**/*.mjs', '**/*.cjs', '**/*.jsx', '**/*.astro'],\n },\n\n // No JSDoc for TypeScript - types > docs\n // TypeScript signatures provide better documentation than JSDoc\n\n // TypeScript-specific rule overrides for LLM code\n // Only applies to TS files (JS files don't have type info for these rules)\n {\n name: 'safeword/typescript-rules',\n files: TS_FILES,\n rules: {\n // consistent-type-definitions: Disabled because both interface and type are valid:\n // - interface: Better error messages, extendable, declaration merging\n // - type: Union/intersection types, mapped types, template literals\n // Forcing one over the other limits expressiveness. Teams should choose per-case.\n // See: https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces\n '@typescript-eslint/consistent-type-definitions': 'off',\n\n // LLMs use `any` when stuck - force them to use `unknown` instead\n '@typescript-eslint/no-explicit-any': 'error',\n\n // LLMs use truthy checks when they should be explicit\n // This catches bugs like `if (count)` when count could be 0\n '@typescript-eslint/strict-boolean-expressions': [\n 'error',\n {\n allowString: true, // Allow string checks (common pattern)\n allowNumber: false, // Disallow number checks (0 is falsy bug)\n allowNullableObject: true,\n allowNullableBoolean: true,\n allowNullableString: true,\n allowNullableNumber: false,\n allowAny: false,\n },\n ],\n\n // Underscore prefix convention for intentionally unused vars (matches TypeScript behavior)\n '@typescript-eslint/no-unused-vars': [\n 'error',\n {\n argsIgnorePattern: '^_',\n varsIgnorePattern: '^_',\n caughtErrorsIgnorePattern: '^_',\n },\n ],\n\n // Design rules not in strict+stylistic (high LLM value)\n '@typescript-eslint/consistent-type-imports': 'error', // import type { X } for types\n '@typescript-eslint/switch-exhaustiveness-check': 'error', // Missing case in union switch\n '@typescript-eslint/no-shadow': 'error', // Variable shadows outer scope\n '@typescript-eslint/require-array-sort-compare': 'error', // [].sort() needs compareFn\n '@typescript-eslint/no-unused-private-class-members': 'error', // Catch dead code in classes\n },\n },\n\n // Prettier must be last to disable conflicting rules\n prettierConfig,\n\n // Re-enable curly after prettier (prettier turns it off but we want braces for LLM code)\n {\n name: 'safeword/post-prettier',\n rules: {\n curly: 'error', // Force braces on if/else/for/while - LLMs write unsafe single-line blocks\n },\n },\n];\n","/**\n * ESLint configuration for Storybook stories\n *\n * Uses official flat/recommended as base, then:\n * - Upgrades all warn → error (LLMs ignore warnings)\n * - Adds strict rules not in recommended\n *\n * @see https://github.com/storybookjs/eslint-plugin-storybook\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport storybookPlugin from 'eslint-plugin-storybook';\n\n/**\n * Storybook story linting config\n *\n * Based on flat/recommended with stricter settings for LLM code generation.\n */\nexport const storybookConfig: any[] = [\n // Use official flat/recommended as base (includes plugin setup + file patterns)\n ...storybookPlugin.configs['flat/recommended'],\n\n // Override warnings to errors + add strict rules\n {\n name: 'safeword/storybook-strict',\n files: ['**/*.stories.{ts,tsx,js,jsx,mjs,cjs}', '**/*.story.{ts,tsx,js,jsx,mjs,cjs}'],\n rules: {\n // Upgrade warn → error (LLMs ignore warnings)\n 'storybook/hierarchy-separator': 'error',\n 'storybook/no-redundant-story-name': 'error',\n 'storybook/prefer-pascal-case': 'error',\n\n // Strict rules not in recommended (useful for LLMs)\n 'storybook/csf-component': 'error', // component property should be set\n 'storybook/no-stories-of': 'error', // storiesOf is deprecated\n 'storybook/meta-inline-properties': 'error', // Meta should only have inline properties\n },\n },\n];\n","/**\n * ESLint configuration for Tailwind CSS projects\n *\n * Uses eslint-plugin-better-tailwindcss for native Tailwind v4 support.\n * All rules enabled at error level (LLMs ignore warnings).\n *\n * Includes 11 rules:\n * - Correctness: no-conflicting-classes, no-unregistered-classes, no-restricted-classes\n * - Stylistic: enforce-consistent-class-order, enforce-shorthand-classes, no-duplicate-classes,\n * no-deprecated-classes, enforce-consistent-line-wrapping, no-unnecessary-whitespace,\n * enforce-consistent-variable-syntax, enforce-consistent-important-position\n *\n * @see https://github.com/schoero/eslint-plugin-better-tailwindcss\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport eslintPluginBetterTailwindcss from 'eslint-plugin-better-tailwindcss';\n\n/** File patterns for Tailwind rules - targets files containing Tailwind classes */\nexport const TAILWIND_FILES = ['**/*.{jsx,tsx,astro,html}'];\n\nexport const tailwindConfig: any[] = [\n {\n name: 'safeword/tailwind',\n files: TAILWIND_FILES,\n plugins: {\n 'better-tailwindcss': eslintPluginBetterTailwindcss,\n },\n rules: {\n // Correctness rules - catch LLM mistakes\n 'better-tailwindcss/no-conflicting-classes': 'error',\n 'better-tailwindcss/no-unregistered-classes': 'error',\n 'better-tailwindcss/no-restricted-classes': 'error', // no-op by default, configurable\n\n // Stylistic rules - enforce consistency\n 'better-tailwindcss/enforce-consistent-class-order': 'error',\n 'better-tailwindcss/enforce-consistent-line-wrapping': 'error',\n 'better-tailwindcss/enforce-consistent-variable-syntax': 'error',\n 'better-tailwindcss/enforce-consistent-important-position': 'error',\n 'better-tailwindcss/enforce-shorthand-classes': 'error',\n 'better-tailwindcss/no-duplicate-classes': 'error',\n 'better-tailwindcss/no-deprecated-classes': 'error',\n 'better-tailwindcss/no-unnecessary-whitespace': 'error',\n },\n },\n];\n","/**\n * ESLint configuration for TanStack Query\n *\n * Enforces best practices for TanStack Query (React Query).\n * All 7 rules at error severity - LLMs have no valid reason to violate these.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport tanstackQueryPlugin from '@tanstack/eslint-plugin-query';\n\n/**\n * TanStack Query linting config\n *\n * All rules at error severity:\n * - exhaustive-deps: Missing query key deps → stale cached data\n * - stable-query-client: Client in component → infinite re-render loop\n * - no-void-query-fn: No return → undefined cached\n * - no-rest-destructuring: ...rest loses reactivity\n * - no-unstable-deps: New function each render → unnecessary refetches\n * - infinite-query-property-order: Wrong order → TypeScript can't infer types\n * - mutation-property-order: Wrong order → TypeScript can't infer types\n */\nexport const tanstackQueryConfig: any[] = [\n {\n name: 'safeword/tanstack-query',\n plugins: {\n '@tanstack/query': tanstackQueryPlugin,\n },\n rules: {\n '@tanstack/query/exhaustive-deps': 'error',\n '@tanstack/query/stable-query-client': 'error',\n '@tanstack/query/no-void-query-fn': 'error',\n '@tanstack/query/no-rest-destructuring': 'error',\n '@tanstack/query/no-unstable-deps': 'error',\n '@tanstack/query/infinite-query-property-order': 'error',\n '@tanstack/query/mutation-property-order': 'error',\n },\n },\n];\n","/**\n * ESLint configuration for Turborepo projects\n *\n * Ensures environment variables used in code are declared in turbo.json\n * for proper cache invalidation.\n *\n * @see https://turbo.build/repo/docs/reference/eslint-plugin-turbo\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport turboPlugin from 'eslint-plugin-turbo';\n\n/**\n * Turborepo env var validation config\n *\n * Uses official flat/recommended preset (already at error severity).\n * Catches undeclared env vars that would break Turborepo caching.\n */\nexport const turboConfig: any[] = [turboPlugin.configs['flat/recommended']];\n","/**\n * ESLint configuration for Vitest tests\n *\n * Applies to test files: *.test.ts, *.spec.ts\n * Enforces test best practices for LLM-generated tests.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport vitestPlugin from 'eslint-plugin-vitest';\n\n/**\n * Vitest test linting config\n *\n * Includes recommended rules plus no-focused-tests.\n * All rules at error severity.\n */\nexport const vitestConfig: any[] = [\n {\n name: 'safeword/vitest',\n files: ['**/*.test.{ts,tsx,js,jsx}', '**/*.spec.{ts,tsx,js,jsx}'],\n plugins: {\n vitest: vitestPlugin,\n },\n rules: {\n // Recommended rules (all at error)\n // Allow expect* helper functions (e.g., expectErrorSeverity) as assertion functions\n 'vitest/expect-expect': ['error', { assertFunctionNames: ['expect', 'expect*'] }],\n 'vitest/no-identical-title': 'error',\n 'vitest/no-commented-out-tests': 'error',\n 'vitest/valid-title': 'error',\n 'vitest/valid-expect': ['error', { maxArgs: 2 }], // Allow custom message: expect(value, 'message')\n 'vitest/valid-describe-callback': 'error',\n 'vitest/require-local-test-context-for-concurrent-snapshots': 'error',\n 'vitest/no-import-node-test': 'error',\n\n // Additional strict rules\n 'vitest/no-focused-tests': 'error', // No .only() in CI\n 'vitest/max-nested-describe': ['error', { max: 5 }], // Limit describe nesting depth\n\n // Relax base rules for test files - each override has documented justification:\n //\n // no-empty-function: Tests often need empty callbacks for mocks/stubs:\n // const mockFn = vi.fn(() => {}); // Valid mock with no implementation\n // await expect(action).rejects.toThrow(); // Empty catch in expect wrapper\n '@typescript-eslint/no-empty-function': 'off',\n //\n // detect-non-literal-fs-filename: Tests read fixtures from known safe paths:\n // const fixture = readFileSync(join(__dirname, 'fixtures', testCase.input));\n // Test fixtures are developer-controlled, not user input.\n 'security/detect-non-literal-fs-filename': 'off',\n // Keep max-nested-callbacks at reasonable threshold for tests.\n // Default is 10; we use 6 to catch excessive nesting early while allowing\n // typical patterns like: describe → it → array.filter → callback.\n 'max-nested-callbacks': ['error', { max: 6 }],\n },\n },\n];\n","/**\n * TypeScript Preset\n *\n * ESLint configs, rules, and detection for TypeScript/JavaScript projects.\n * This is the main entry point for the TypeScript language preset.\n *\n * Usage in user's eslint.config.mjs:\n * import safeword from 'safeword/eslint';\n * export default [...safeword.configs.recommendedTypeScript];\n *\n * Or with multiple configs:\n * import safeword from 'safeword/eslint';\n * export default [\n * ...safeword.configs.recommendedTypeScript,\n * ...safeword.configs.vitest,\n * ];\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport type { Rule } from 'eslint';\n\nimport { VERSION } from '../../version.js';\nimport { detect } from './detect.js';\nimport { astroConfig } from './eslint-configs/astro.js';\nimport { prettierConfig } from './eslint-configs/base.js';\nimport { playwrightConfig } from './eslint-configs/playwright.js';\nimport { recommended } from './eslint-configs/recommended.js';\nimport { nextOnlyRules, recommendedTypeScriptNext } from './eslint-configs/recommended-nextjs.js';\nimport { recommendedTypeScriptReact } from './eslint-configs/recommended-react.js';\nimport { recommendedTypeScript } from './eslint-configs/recommended-typescript.js';\nimport { storybookConfig } from './eslint-configs/storybook.js';\nimport { tailwindConfig } from './eslint-configs/tailwind.js';\nimport { tanstackQueryConfig } from './eslint-configs/tanstack-query.js';\nimport { turboConfig } from './eslint-configs/turbo.js';\nimport { vitestConfig } from './eslint-configs/vitest.js';\nimport { rules } from './eslint-rules/index.js';\n\ninterface SafewordEslint {\n meta: {\n name: string;\n version: string;\n };\n configs: {\n recommended: any[];\n recommendedTypeScript: any[];\n recommendedTypeScriptReact: any[];\n recommendedTypeScriptNext: any[];\n /** Next.js-only rules for monorepo file scoping */\n nextOnlyRules: any[];\n astro: any[];\n tailwind: any[];\n tanstackQuery: any[];\n vitest: any[];\n playwright: any[];\n storybook: any[];\n turbo: any[];\n };\n detect: typeof detect;\n rules: Record<string, Rule.RuleModule>;\n /** eslint-config-prettier, bundled for convenience */\n prettierConfig: any;\n}\n\n/**\n * ESLint plugin structure for TypeScript preset.\n * Can be used directly as an ESLint plugin or via safeword.eslint.\n */\nexport const eslintPlugin: SafewordEslint = {\n meta: {\n name: 'safeword',\n version: VERSION,\n },\n configs: {\n recommended,\n recommendedTypeScript,\n recommendedTypeScriptReact,\n recommendedTypeScriptNext,\n nextOnlyRules,\n astro: astroConfig,\n tailwind: tailwindConfig,\n tanstackQuery: tanstackQueryConfig,\n vitest: vitestConfig,\n playwright: playwrightConfig,\n storybook: storybookConfig,\n turbo: turboConfig,\n },\n detect,\n rules,\n prettierConfig,\n};\n\n// Re-export configs for direct access\nexport { detect } from './detect.js';\nexport { astroConfig } from './eslint-configs/astro.js';\nexport { prettierConfig } from './eslint-configs/base.js';\nexport { playwrightConfig } from './eslint-configs/playwright.js';\nexport { recommended } from './eslint-configs/recommended.js';\nexport { nextOnlyRules, recommendedTypeScriptNext } from './eslint-configs/recommended-nextjs.js';\nexport { recommendedTypeScriptReact } from './eslint-configs/recommended-react.js';\nexport { recommendedTypeScript } from './eslint-configs/recommended-typescript.js';\nexport { storybookConfig } from './eslint-configs/storybook.js';\nexport { tailwindConfig } from './eslint-configs/tailwind.js';\nexport { tanstackQueryConfig } from './eslint-configs/tanstack-query.js';\nexport { turboConfig } from './eslint-configs/turbo.js';\nexport { vitestConfig } from './eslint-configs/vitest.js';\nexport { rules } from './eslint-rules/index.js';\n\n// Default export for `import safeword from \"safeword/eslint\"`\nexport default eslintPlugin;\n"],"mappings":"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","names":["rule","rule","default","default","importX","importX","default","default"]}
@@ -10,46 +10,16 @@ var TANSTACK_QUERY_PACKAGES = [
10
10
  ];
11
11
  var TAILWIND_PACKAGES = ["tailwindcss", "@tailwindcss/vite", "@tailwindcss/postcss"];
12
12
  var PLAYWRIGHT_PACKAGES = ["@playwright/test", "playwright"];
13
- var FORMATTER_CONFIG_FILES = [
14
- // Biome
13
+ var ALTERNATIVE_FORMATTER_FILES = [
14
+ // Biome (and legacy Rome)
15
15
  "biome.json",
16
16
  "biome.jsonc",
17
- // dprint
18
- "dprint.json",
19
- ".dprint.json",
20
- "dprint.jsonc",
21
- ".dprint.jsonc",
22
- // Rome (legacy, now Biome)
23
17
  "rome.json",
24
- // Prettier - all supported config formats
25
- // See: https://prettier.io/docs/en/configuration.html
26
- ".prettierrc",
27
- ".prettierrc.json",
28
- ".prettierrc.json5",
29
- ".prettierrc.yaml",
30
- ".prettierrc.yml",
31
- ".prettierrc.toml",
32
- ".prettierrc.js",
33
- ".prettierrc.cjs",
34
- ".prettierrc.mjs",
35
- ".prettierrc.ts",
36
- ".prettierrc.cts",
37
- ".prettierrc.mts",
38
- "prettier.config.js",
39
- "prettier.config.cjs",
40
- "prettier.config.mjs",
41
- "prettier.config.ts",
42
- "prettier.config.cts",
43
- "prettier.config.mts"
44
- ];
45
- var NON_PRETTIER_FORMATTER_FILES = [
46
- "biome.json",
47
- "biome.jsonc",
18
+ // dprint
48
19
  "dprint.json",
49
20
  ".dprint.json",
50
21
  "dprint.jsonc",
51
- ".dprint.jsonc",
52
- "rome.json"
22
+ ".dprint.jsonc"
53
23
  ];
54
24
  function readPackageDeps(pkgPath) {
55
25
  try {
@@ -73,6 +43,12 @@ function getWorkspacePatternsFromPackage(rootPackagePath) {
73
43
  }
74
44
  return [];
75
45
  }
46
+ function getMonorepoPatterns(rootDirectory) {
47
+ const rootPackagePath = path.join(rootDirectory, "package.json");
48
+ const workspacePatterns = getWorkspacePatternsFromPackage(rootPackagePath);
49
+ const commonPatterns = ["apps/*", "packages/*"];
50
+ return [.../* @__PURE__ */ new Set([...workspacePatterns, ...commonPatterns])];
51
+ }
76
52
  function scanWorkspaceDirectory(rootDirectory, pattern) {
77
53
  if (!pattern.endsWith("/*")) return {};
78
54
  const baseDirectory = path.join(rootDirectory, pattern.slice(0, -2));
@@ -95,10 +71,7 @@ function scanWorkspaceDirectory(rootDirectory, pattern) {
95
71
  function collectAllDeps(rootDirectory) {
96
72
  const rootPackagePath = path.join(rootDirectory, "package.json");
97
73
  const allDeps = readPackageDeps(rootPackagePath);
98
- const workspacePatterns = getWorkspacePatternsFromPackage(rootPackagePath);
99
- const commonPatterns = ["apps/*", "packages/*"];
100
- const patterns = [.../* @__PURE__ */ new Set([...workspacePatterns, ...commonPatterns])];
101
- for (const pattern of patterns) {
74
+ for (const pattern of getMonorepoPatterns(rootDirectory)) {
102
75
  Object.assign(allDeps, scanWorkspaceDirectory(rootDirectory, pattern));
103
76
  }
104
77
  return allDeps;
@@ -115,10 +88,13 @@ function hasVitest(deps) {
115
88
  function hasPlaywright(deps) {
116
89
  return PLAYWRIGHT_PACKAGES.some((pkg) => pkg in deps);
117
90
  }
91
+ function hasTurbo(deps) {
92
+ return "turbo" in deps;
93
+ }
118
94
  function detectFramework(deps) {
119
95
  if ("next" in deps) return "next";
120
- if ("react" in deps) return "react";
121
96
  if ("astro" in deps) return "astro";
97
+ if ("react" in deps) return "react";
122
98
  if ("typescript" in deps || "typescript-eslint" in deps) return "typescript";
123
99
  return "javascript";
124
100
  }
@@ -132,15 +108,46 @@ function hasExistingLinter(scripts) {
132
108
  return "lint" in scripts;
133
109
  }
134
110
  function hasExistingFormatter(cwd, _scripts) {
135
- return NON_PRETTIER_FORMATTER_FILES.some((file) => existsSync(path.join(cwd, file)));
111
+ return ALTERNATIVE_FORMATTER_FILES.some((file) => existsSync(path.join(cwd, file)));
112
+ }
113
+ var NEXT_CONFIG_FILES = ["next.config.js", "next.config.mjs", "next.config.ts"];
114
+ function hasNextConfig(directory) {
115
+ return NEXT_CONFIG_FILES.some((file) => existsSync(path.join(directory, file)));
116
+ }
117
+ function scanDirectoryForNextConfigs(rootDirectory, workspacePattern) {
118
+ if (!workspacePattern.endsWith("/*")) return [];
119
+ const baseDirectory = workspacePattern.slice(0, -2);
120
+ const fullBaseDirectory = path.join(rootDirectory, baseDirectory);
121
+ if (!existsSync(fullBaseDirectory)) return [];
122
+ const nextPaths = [];
123
+ try {
124
+ const entries = readdirSync(fullBaseDirectory, { withFileTypes: true });
125
+ for (const entry of entries) {
126
+ if (!entry.isDirectory()) continue;
127
+ const packageDirectory = path.join(fullBaseDirectory, entry.name);
128
+ if (hasNextConfig(packageDirectory)) {
129
+ nextPaths.push(`${baseDirectory}/${entry.name}/**/*.{ts,tsx}`);
130
+ }
131
+ }
132
+ } catch {
133
+ }
134
+ return nextPaths;
135
+ }
136
+ function findNextConfigPaths(rootDirectory) {
137
+ if (hasNextConfig(rootDirectory)) {
138
+ return void 0;
139
+ }
140
+ return getMonorepoPatterns(rootDirectory).flatMap(
141
+ (pattern) => scanDirectoryForNextConfigs(rootDirectory, pattern)
142
+ );
136
143
  }
137
144
  var detect = {
138
145
  // Package lists
139
146
  TAILWIND_PACKAGES,
140
147
  TANSTACK_QUERY_PACKAGES,
141
148
  PLAYWRIGHT_PACKAGES,
142
- FORMATTER_CONFIG_FILES,
143
- NON_PRETTIER_FORMATTER_FILES,
149
+ ALTERNATIVE_FORMATTER_FILES,
150
+ NEXT_CONFIG_FILES,
144
151
  // Core utilities
145
152
  collectAllDeps,
146
153
  detectFramework,
@@ -150,6 +157,9 @@ var detect = {
150
157
  hasTanstackQuery,
151
158
  hasVitest,
152
159
  hasPlaywright,
160
+ hasTurbo,
161
+ // Monorepo detection
162
+ findNextConfigPaths,
153
163
  // Existing tooling detection
154
164
  hasExistingLinter,
155
165
  hasExistingFormatter
@@ -158,4 +168,4 @@ var detect = {
158
168
  export {
159
169
  detect
160
170
  };
161
- //# sourceMappingURL=chunk-OYBKTOVF.js.map
171
+ //# sourceMappingURL=chunk-PMYWVDOF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presets/typescript/detect.ts"],"sourcesContent":["/**\n * Framework detection utilities for safeword ESLint preset\n *\n * Single source of truth for package detection. Used by:\n * - Generated eslint.config.mjs at lint time\n * - CLI's project-detector at init time\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\n\n/**\n * TanStack Query package names across all supported frameworks.\n */\nconst TANSTACK_QUERY_PACKAGES = [\n '@tanstack/react-query',\n '@tanstack/vue-query',\n '@tanstack/solid-query',\n '@tanstack/svelte-query',\n '@tanstack/angular-query-experimental',\n] as const;\n\n/**\n * Tailwind CSS package names (v3 and v4 installation methods).\n */\nconst TAILWIND_PACKAGES = ['tailwindcss', '@tailwindcss/vite', '@tailwindcss/postcss'] as const;\n\n/**\n * Playwright test package names.\n */\nconst PLAYWRIGHT_PACKAGES = ['@playwright/test', 'playwright'] as const;\n\n/**\n * Non-Prettier formatter config files (Biome, dprint, Rome).\n * Used to detect if project uses an alternative formatter.\n * Prettier is safeword's default, so its presence doesn't skip config creation.\n */\nconst ALTERNATIVE_FORMATTER_FILES = [\n // Biome (and legacy Rome)\n 'biome.json',\n 'biome.jsonc',\n 'rome.json',\n // dprint\n 'dprint.json',\n '.dprint.json',\n 'dprint.jsonc',\n '.dprint.jsonc',\n] as const;\n\ntype DepsRecord = Record<string, string | undefined>;\ntype ScriptsRecord = Record<string, string | undefined>;\n\n/**\n * Read dependencies from a package.json file.\n */\nfunction readPackageDeps(pkgPath: string): DepsRecord {\n try {\n if (!existsSync(pkgPath)) return {};\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));\n return { ...pkg.dependencies, ...pkg.devDependencies };\n } catch {\n return {};\n }\n}\n\n/**\n * Get workspace patterns from root package.json.\n */\nfunction getWorkspacePatternsFromPackage(rootPackagePath: string): string[] {\n try {\n const rootPackage = JSON.parse(readFileSync(rootPackagePath, 'utf8'));\n if (Array.isArray(rootPackage.workspaces)) {\n return rootPackage.workspaces;\n }\n if (rootPackage.workspaces?.packages) {\n return rootPackage.workspaces.packages;\n }\n } catch {\n // No workspaces defined\n }\n return [];\n}\n\n/**\n * Get all monorepo workspace patterns (from package.json + common directories).\n */\nfunction getMonorepoPatterns(rootDirectory: string): string[] {\n const rootPackagePath = path.join(rootDirectory, 'package.json');\n const workspacePatterns = getWorkspacePatternsFromPackage(rootPackagePath);\n const commonPatterns = ['apps/*', 'packages/*'];\n return [...new Set([...workspacePatterns, ...commonPatterns])];\n}\n\n/**\n * Scan a workspace directory for package.json files.\n */\nfunction scanWorkspaceDirectory(rootDirectory: string, pattern: string): DepsRecord {\n if (!pattern.endsWith('/*')) return {};\n\n const baseDirectory = path.join(rootDirectory, pattern.slice(0, -2));\n if (!existsSync(baseDirectory)) return {};\n\n const allDeps: DepsRecord = {};\n try {\n const entries = readdirSync(baseDirectory, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n Object.assign(\n allDeps,\n readPackageDeps(path.join(baseDirectory, entry.name, 'package.json')),\n );\n }\n }\n } catch {\n // Ignore read errors\n }\n return allDeps;\n}\n\n/**\n * Collect all dependencies from root and workspace package.json files.\n * Supports npm/yarn workspaces and common monorepo patterns.\n */\nfunction collectAllDeps(rootDirectory: string): DepsRecord {\n const rootPackagePath = path.join(rootDirectory, 'package.json');\n const allDeps = readPackageDeps(rootPackagePath);\n\n // Scan each workspace pattern\n for (const pattern of getMonorepoPatterns(rootDirectory)) {\n Object.assign(allDeps, scanWorkspaceDirectory(rootDirectory, pattern));\n }\n\n return allDeps;\n}\n\n/**\n * Check if Tailwind CSS is installed.\n */\nfunction hasTailwind(deps: DepsRecord): boolean {\n return TAILWIND_PACKAGES.some(pkg => pkg in deps);\n}\n\n/**\n * Check if TanStack Query is installed.\n */\nfunction hasTanstackQuery(deps: DepsRecord): boolean {\n return TANSTACK_QUERY_PACKAGES.some(pkg => pkg in deps);\n}\n\n/**\n * Check if Vitest is installed.\n */\nfunction hasVitest(deps: DepsRecord): boolean {\n return 'vitest' in deps;\n}\n\n/**\n * Check if Playwright is installed.\n */\nfunction hasPlaywright(deps: DepsRecord): boolean {\n return PLAYWRIGHT_PACKAGES.some(pkg => pkg in deps);\n}\n\n/**\n * Check if Turborepo is installed.\n */\nfunction hasTurbo(deps: DepsRecord): boolean {\n return 'turbo' in deps;\n}\n\n/**\n * Detect base framework for config selection.\n * Returns the most specific framework detected.\n */\nfunction detectFramework(\n deps: DepsRecord,\n): 'next' | 'react' | 'astro' | 'typescript' | 'javascript' {\n if ('next' in deps) return 'next';\n if ('astro' in deps) return 'astro'; // Check before React (Astro+React has both)\n if ('react' in deps) return 'react';\n if ('typescript' in deps || 'typescript-eslint' in deps) return 'typescript';\n return 'javascript';\n}\n\n/**\n * Get dynamic ignores based on detected frameworks.\n */\nfunction getIgnores(deps: DepsRecord): string[] {\n const ignores = ['**/node_modules/', '**/dist/', '**/build/', '**/coverage/'];\n if ('next' in deps) ignores.push('**/.next/');\n if ('astro' in deps) ignores.push('**/.astro/');\n return ignores;\n}\n\n/**\n * Check if project has an existing linter setup.\n * True if package.json has a \"lint\" script.\n */\nfunction hasExistingLinter(scripts: ScriptsRecord): boolean {\n return 'lint' in scripts;\n}\n\n/**\n * Check if project uses an alternative formatter (Biome, dprint, Rome).\n * Returns false for Prettier since it's safeword's default.\n *\n * We don't check for \"format\" script because safeword adds that itself.\n */\nfunction hasExistingFormatter(cwd: string, _scripts: ScriptsRecord): boolean {\n return ALTERNATIVE_FORMATTER_FILES.some(file => existsSync(path.join(cwd, file)));\n}\n\n/**\n * Next.js config file names to look for.\n */\nconst NEXT_CONFIG_FILES = ['next.config.js', 'next.config.mjs', 'next.config.ts'] as const;\n\n/**\n * Check if a directory contains a Next.js config file.\n */\nfunction hasNextConfig(directory: string): boolean {\n return NEXT_CONFIG_FILES.some(file => existsSync(path.join(directory, file)));\n}\n\n/**\n * Scan a workspace directory for Next.js config files.\n * Returns relative glob patterns for directories containing Next.js configs.\n */\nfunction scanDirectoryForNextConfigs(rootDirectory: string, workspacePattern: string): string[] {\n if (!workspacePattern.endsWith('/*')) return [];\n\n const baseDirectory = workspacePattern.slice(0, -2); // Remove '/*'\n const fullBaseDirectory = path.join(rootDirectory, baseDirectory);\n\n if (!existsSync(fullBaseDirectory)) return [];\n\n const nextPaths: string[] = [];\n try {\n const entries = readdirSync(fullBaseDirectory, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const packageDirectory = path.join(fullBaseDirectory, entry.name);\n if (hasNextConfig(packageDirectory)) {\n nextPaths.push(`${baseDirectory}/${entry.name}/**/*.{ts,tsx}`);\n }\n }\n } catch {\n // Ignore read errors\n }\n return nextPaths;\n}\n\n/**\n * Find Next.js config paths for ESLint rule scoping in monorepos.\n *\n * Returns:\n * - `undefined` if Next.js config exists at root (single app, no scoping needed)\n * - `string[]` of glob patterns for directories containing Next.js apps\n * - Empty array if no Next.js configs found anywhere\n *\n * Used to scope Next.js-specific ESLint rules to only the packages that use Next.js,\n * allowing other packages (React, Astro, etc.) to avoid irrelevant Next.js rules.\n */\nfunction findNextConfigPaths(rootDirectory: string): string[] | undefined {\n // Check for root Next.js config - means single app, no scoping needed\n if (hasNextConfig(rootDirectory)) {\n return undefined;\n }\n\n // Scan each workspace pattern for Next.js configs\n return getMonorepoPatterns(rootDirectory).flatMap(pattern =>\n scanDirectoryForNextConfigs(rootDirectory, pattern),\n );\n}\n\n/**\n * All detection utilities bundled together.\n */\nexport const detect = {\n // Package lists\n TAILWIND_PACKAGES,\n TANSTACK_QUERY_PACKAGES,\n PLAYWRIGHT_PACKAGES,\n ALTERNATIVE_FORMATTER_FILES,\n NEXT_CONFIG_FILES,\n\n // Core utilities\n collectAllDeps,\n detectFramework,\n getIgnores,\n\n // Feature detection\n hasTailwind,\n hasTanstackQuery,\n hasVitest,\n hasPlaywright,\n hasTurbo,\n\n // Monorepo detection\n findNextConfigPaths,\n\n // Existing tooling detection\n hasExistingLinter,\n hasExistingFormatter,\n};\n"],"mappings":"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","names":[]}