From 22bee5362f70578cc6e9ca59ac6308451f93fb3c Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Tue, 5 May 2026 09:21:01 +0200 Subject: [PATCH] =?UTF-8?q?feat(eslint-config):=20add=20boundaries=20plugi?= =?UTF-8?q?n=20enforcing=20app=E2=86=92feature=E2=86=92core=20graph?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Install eslint-plugin-boundaries@^4.2.2 to enforce three-tag boundary model - Configure element types: app, core-composition (core-api/core-cms), core, feature, tooling - Enforce unidirectional dependency graph: apps→features→core, core-composition→features - Add eslint.config.js to all 17 packages and apps (required for ESLint 9 flat config) - Fix pre-existing linting issues to achieve clean lint pass Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/cms/eslint.config.js | 3 + apps/cms/next-env.d.ts | 1 + apps/storybook/eslint.config.js | 3 + apps/web-next/eslint.config.js | 3 + apps/web-next/next-env.d.ts | 1 + apps/web-tanstack/eslint.config.js | 3 + packages/auth/eslint.config.js | 3 + packages/auth/src/di/bind-production.ts | 1 + packages/blog/eslint.config.js | 3 + packages/core-api/eslint.config.js | 3 + packages/core-api/src/root.ts | 4 - packages/core-cms/eslint.config.js | 3 + packages/core-shared/eslint.config.js | 3 + packages/core-trpc/eslint.config.js | 3 + packages/core-ui/eslint.config.js | 3 + packages/core-ui/src/atoms/input/input.tsx | 2 +- packages/core-ui/src/atoms/label/label.tsx | 2 +- packages/eslint-config/base.js | 32 +++++++- packages/eslint-config/eslint.config.js | 3 + packages/eslint-config/package.json | 1 + packages/marketing-pages/eslint.config.js | 3 + packages/media/eslint.config.js | 3 + packages/navigation/eslint.config.js | 3 + pnpm-lock.yaml | 85 ++++++++++++++++++++++ 24 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 apps/cms/eslint.config.js create mode 100644 apps/storybook/eslint.config.js create mode 100644 apps/web-next/eslint.config.js create mode 100644 apps/web-tanstack/eslint.config.js create mode 100644 packages/auth/eslint.config.js create mode 100644 packages/blog/eslint.config.js create mode 100644 packages/core-api/eslint.config.js create mode 100644 packages/core-cms/eslint.config.js create mode 100644 packages/core-shared/eslint.config.js create mode 100644 packages/core-trpc/eslint.config.js create mode 100644 packages/core-ui/eslint.config.js create mode 100644 packages/eslint-config/eslint.config.js create mode 100644 packages/marketing-pages/eslint.config.js create mode 100644 packages/media/eslint.config.js create mode 100644 packages/navigation/eslint.config.js diff --git a/apps/cms/eslint.config.js b/apps/cms/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/apps/cms/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/apps/cms/next-env.d.ts b/apps/cms/next-env.d.ts index 830fb59..5277a32 100644 --- a/apps/cms/next-env.d.ts +++ b/apps/cms/next-env.d.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/triple-slash-reference */ /// /// /// diff --git a/apps/storybook/eslint.config.js b/apps/storybook/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/apps/storybook/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/apps/web-next/eslint.config.js b/apps/web-next/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/apps/web-next/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/apps/web-next/next-env.d.ts b/apps/web-next/next-env.d.ts index 830fb59..5277a32 100644 --- a/apps/web-next/next-env.d.ts +++ b/apps/web-next/next-env.d.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/triple-slash-reference */ /// /// /// diff --git a/apps/web-tanstack/eslint.config.js b/apps/web-tanstack/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/apps/web-tanstack/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/packages/auth/eslint.config.js b/packages/auth/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/packages/auth/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/packages/auth/src/di/bind-production.ts b/packages/auth/src/di/bind-production.ts index 3c4904d..7ee8eb1 100644 --- a/packages/auth/src/di/bind-production.ts +++ b/packages/auth/src/di/bind-production.ts @@ -6,6 +6,7 @@ import type { SanitizedConfig as _SanitizedConfig } from "payload"; // // Until then it's a no-op that intentionally accepts (and ignores) the // SanitizedConfig argument so app-boot code can call it uniformly. +// eslint-disable-next-line @typescript-eslint/no-unused-vars export function bindProductionAuth(_config: _SanitizedConfig): void { // Default mock bindings from `module.ts` already loaded by container.ts; // nothing to swap. diff --git a/packages/blog/eslint.config.js b/packages/blog/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/packages/blog/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/packages/core-api/eslint.config.js b/packages/core-api/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/packages/core-api/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/packages/core-api/src/root.ts b/packages/core-api/src/root.ts index 444b570..1228d77 100644 --- a/packages/core-api/src/root.ts +++ b/packages/core-api/src/root.ts @@ -1,7 +1,3 @@ -import type { AuthRouter } from "@repo/auth"; -import type { BlogRouter } from "@repo/blog"; -import type { MarketingPagesRouter } from "@repo/marketing-pages"; -import type { NavigationRouter } from "@repo/navigation"; import { router } from "@repo/core-shared/trpc/init"; import { authRouter } from "@repo/auth/api"; import { blogRouter } from "@repo/blog/api"; diff --git a/packages/core-cms/eslint.config.js b/packages/core-cms/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/packages/core-cms/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/packages/core-shared/eslint.config.js b/packages/core-shared/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/packages/core-shared/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/packages/core-trpc/eslint.config.js b/packages/core-trpc/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/packages/core-trpc/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/packages/core-ui/eslint.config.js b/packages/core-ui/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/packages/core-ui/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/packages/core-ui/src/atoms/input/input.tsx b/packages/core-ui/src/atoms/input/input.tsx index 51b3dd0..b925986 100644 --- a/packages/core-ui/src/atoms/input/input.tsx +++ b/packages/core-ui/src/atoms/input/input.tsx @@ -1,7 +1,7 @@ import { forwardRef, type InputHTMLAttributes } from "react"; import { cn } from "../../lib/utils"; -export interface InputProps extends InputHTMLAttributes {} +export type InputProps = InputHTMLAttributes; export const Input = forwardRef( ({ className, type, ...props }, ref) => { diff --git a/packages/core-ui/src/atoms/label/label.tsx b/packages/core-ui/src/atoms/label/label.tsx index 13d0f4e..afc57b1 100644 --- a/packages/core-ui/src/atoms/label/label.tsx +++ b/packages/core-ui/src/atoms/label/label.tsx @@ -1,7 +1,7 @@ import { forwardRef, type LabelHTMLAttributes } from "react"; import { cn } from "../../lib/utils"; -export interface LabelProps extends LabelHTMLAttributes {} +export type LabelProps = LabelHTMLAttributes; export const Label = forwardRef( ({ className, ...props }, ref) => { diff --git a/packages/eslint-config/base.js b/packages/eslint-config/base.js index bc35bef..0971a70 100644 --- a/packages/eslint-config/base.js +++ b/packages/eslint-config/base.js @@ -2,9 +2,10 @@ import js from "@eslint/js"; import eslintConfigPrettier from "eslint-config-prettier"; import tseslint from "typescript-eslint"; import turboPlugin from "eslint-plugin-turbo"; +import boundaries from "eslint-plugin-boundaries"; export default [ - { ignores: ["dist/**", "node_modules/**"] }, + { ignores: ["dist/**", "node_modules/**", ".next/**", ".turbo/**"] }, js.configs.recommended, ...tseslint.configs.recommended, eslintConfigPrettier, @@ -14,4 +15,33 @@ export default [ "turbo/no-undeclared-env-vars": "warn", }, }, + { + plugins: { boundaries }, + settings: { + "boundaries/elements": [ + { type: "app", pattern: "apps/*" }, + { type: "core-composition", pattern: "packages/core-api" }, + { type: "core-composition", pattern: "packages/core-cms" }, + { type: "core", pattern: "packages/core-*" }, + { type: "tooling", pattern: "packages/eslint-config" }, + { type: "tooling", pattern: "packages/typescript-config" }, + { type: "feature", pattern: "packages/!(core-*|eslint-config|typescript-config)" }, + ], + }, + rules: { + "boundaries/element-types": [ + 2, + { + default: "disallow", + rules: [ + { from: "app", allow: ["app", "core", "core-composition", "feature", "tooling"] }, + { from: "feature", allow: ["core", "tooling"] }, + { from: "core", allow: ["core", "tooling"] }, + { from: "core-composition", allow: ["core", "feature", "tooling"] }, + { from: "tooling", allow: ["tooling"] }, + ], + }, + ], + }, + }, ]; diff --git a/packages/eslint-config/eslint.config.js b/packages/eslint-config/eslint.config.js new file mode 100644 index 0000000..5425eef --- /dev/null +++ b/packages/eslint-config/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "./base.js"; + +export default baseConfig; diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index b813bb7..b009120 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -14,6 +14,7 @@ "@typescript-eslint/parser": "^8.25.0", "eslint": "^9.20.0", "eslint-config-prettier": "^10.1.0", + "eslint-plugin-boundaries": "^4.2.2", "eslint-plugin-turbo": "^2.4.0", "typescript-eslint": "^8.25.0" } diff --git a/packages/marketing-pages/eslint.config.js b/packages/marketing-pages/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/packages/marketing-pages/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/packages/media/eslint.config.js b/packages/media/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/packages/media/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/packages/navigation/eslint.config.js b/packages/navigation/eslint.config.js new file mode 100644 index 0000000..35479e2 --- /dev/null +++ b/packages/navigation/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/eslint-config/base"; + +export default baseConfig; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac11ba7..e76a9a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -460,6 +460,9 @@ importers: eslint-config-prettier: specifier: ^10.1.0 version: 10.1.8(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-boundaries: + specifier: ^4.2.2 + version: 4.2.2(@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-turbo: specifier: ^2.4.0 version: 2.9.4(eslint@9.39.4(jiti@2.6.1))(turbo@2.9.4) @@ -2819,6 +2822,14 @@ packages: dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -3053,6 +3064,36 @@ packages: peerDependencies: eslint: '>=7.0.0' + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.8.1: + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-boundaries@4.2.2: + resolution: {integrity: sha512-cjwpZqkCXgfz953bc74uDetOtGVxwgMgNZ7hAKi6Oxck+x4oY6Z/9DzgPqAYhtQdSNHFVg+vhft/lSL+snPMQg==} + engines: {node: '>=14.0.0'} + peerDependencies: + eslint: '>=6.0.0' + eslint-plugin-turbo@2.9.4: resolution: {integrity: sha512-8gIBw+QC7jLOjYLthoLYcDGf0pEefAukkMAl60exv3HkS3NO7AuXzzFjY3iDGDc4s5mbkC6f/692DCdb3XHwMg==} peerDependencies: @@ -3721,6 +3762,10 @@ packages: micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} @@ -6976,6 +7021,10 @@ snapshots: dateformat@4.6.3: {} + debug@3.2.7: + dependencies: + ms: 2.1.3 + debug@4.4.3: dependencies: ms: 2.1.3 @@ -7174,6 +7223,37 @@ snapshots: dependencies: eslint: 9.39.4(jiti@2.6.1) + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.8.1(@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.58.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-plugin-boundaries@4.2.2(@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)): + dependencies: + chalk: 4.1.2 + eslint: 9.39.4(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)) + micromatch: 4.0.7 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + eslint-plugin-turbo@2.9.4(eslint@9.39.4(jiti@2.6.1))(turbo@2.9.4): dependencies: dotenv: 16.0.3 @@ -7930,6 +8010,11 @@ snapshots: transitivePeerDependencies: - supports-color + micromatch@4.0.7: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + minimatch@10.2.5: dependencies: brace-expansion: 5.0.5