dependency-cruiser.config.cjs 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // @ts-check
  2. // Deep-module enforcement for dependency-cruiser.
  3. //
  4. // Each package under the packages root is a DEEP MODULE: a lot of behaviour
  5. // behind a small interface. A package's PUBLIC SURFACE is its ENTRY POINTS —
  6. // the files at the package root. Implementation lives in SUBFOLDERS and is
  7. // private — by convention `lib/` for implementation and `tests/` for tests,
  8. // though any subfolder is private. A package may expose several small entry
  9. // points (index.ts, client.ts, server.ts, …) — prefer that over one giant
  10. // barrel index.
  11. //
  12. // The only thing you should ever need to edit here is PACKAGES_ROOT.
  13. /** Where packages live. One immediate child dir per package (flat, no nesting). */
  14. const PACKAGES_ROOT = "src/packages";
  15. // --- derived patterns (no need to edit) -------------------------------------
  16. const R = PACKAGES_ROOT;
  17. /**
  18. * A package's private internals: anything nested inside a package subfolder.
  19. * The package's root files are its entry points and are NOT matched here —
  20. * they stay importable from outside.
  21. */
  22. const PACKAGE_INTERNALS = `^${R}/[^/]+/[^/]+/`;
  23. /** @type {import('dependency-cruiser').IConfiguration} */
  24. module.exports = {
  25. forbidden: [
  26. {
  27. name: "entrypoint-boundary-from-app",
  28. comment:
  29. "App/root code may import a package's entry points (its root files), but nothing inside its subfolders.",
  30. severity: "error",
  31. from: { pathNot: `^${R}/` }, // importer is NOT inside any package
  32. to: { path: PACKAGE_INTERNALS },
  33. },
  34. {
  35. name: "entrypoint-boundary-across-packages",
  36. comment:
  37. "A package's own files import each other freely, but may reach OTHER packages only through their entry points — never their internals.",
  38. severity: "error",
  39. // importer is inside a package ($1), but is not a test file
  40. from: { path: `^${R}/([^/]+)/`, pathNot: `^${R}/[^/]+/tests/` },
  41. to: {
  42. path: PACKAGE_INTERNALS,
  43. pathNot: `^${R}/$1/`, // same package → intra-package freedom
  44. },
  45. },
  46. {
  47. name: "tests-through-entrypoints",
  48. comment:
  49. "A package's tests exercise it through its entry points like everyone else: they may import any package's entry points and their own tests/ fixtures, but never any package's internals — not even their own.",
  50. severity: "error",
  51. from: { path: `^${R}/([^/]+)/tests/` }, // a test file, in package $1
  52. to: {
  53. path: PACKAGE_INTERNALS,
  54. pathNot: `^${R}/$1/tests/`, // own tests/ fixtures → allowed
  55. },
  56. },
  57. {
  58. name: "tests-folder-is-private",
  59. comment:
  60. "A package's tests/ folder is reachable only from tests — nothing else may import fixtures.",
  61. severity: "error",
  62. from: { pathNot: `^${R}/[^/]+/tests/` }, // importer is not itself a test
  63. to: { path: `^${R}/[^/]+/tests/` },
  64. },
  65. {
  66. name: "no-circular",
  67. comment: "No dependency cycles. Scope to `^${R}/` if you want to allow cycles outside packages.",
  68. severity: "error",
  69. from: {},
  70. to: { circular: true },
  71. },
  72. // --- Layering (optional, off by default) ----------------------------------
  73. // Interface-hiding controls HOW you import (through the entry points).
  74. // Layering controls WHICH packages may depend on which. Add your own rules
  75. // here, e.g.:
  76. //
  77. // {
  78. // name: "ui-may-not-depend-on-billing",
  79. // severity: "error",
  80. // from: { path: `^${R}/ui/` },
  81. // to: { path: `^${R}/billing/` },
  82. // },
  83. ],
  84. options: {
  85. doNotFollow: { path: "node_modules" },
  86. tsConfig: { fileName: "tsconfig.json" },
  87. enhancedResolveOptions: {
  88. extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
  89. },
  90. },
  91. };