javierbrea / eslint-plugin-boundaries

Eslint plugin checking architecture boundaries between elements
MIT License
473 stars 9 forks source link

API to reduce boilerplate in standard monorepo setup - allPackages() utility function #318

Closed tomosterlund closed 4 months ago

tomosterlund commented 7 months ago

First of all, thanks for this plugin. It's brilliant, and I love it!

Is your feature request related to a problem? Please describe. Not really a big problem, but:

I use this package for a monorepo project. Only in a few cases are my npm packages allowed to use modules from other packages in the workspace, which is where this plugin comes in super handy to disallow cross-package dependencies. In a monorepo with a few packages, the boilerplate needed for this in the eslint-config is OK. But as a project grows and includes dozens of packages, it would be nice to try and reduce the boilerplate code required for the eslint settings.

Describe the solution you'd like A single line of code, basically a utility function, to generate elements for this plugin. For example, instead of:

settings: {
    'boundaries/elements': [
      {
        type: 'calendar',
        pattern: 'packages/calendar/*',
      },
      {
        type: 'date-picker',
        pattern: 'packages/date-picker/*',
      },
      {
        type: 'drag-and-drop',
        pattern: 'packages/drag-and-drop/*',
      },
      {
        type: 'event-modal',
        pattern: 'packages/event-modal/*',
      },
      {
        type: 'shared',
        pattern: 'packages/shared/*',
      },
      {
        type: 'translations',
        pattern: 'packages/translations/*',
      },
      // ...and so on
    ],
  },

Something like this would be nice:

settings: {
    'boundaries/elements': [  ...allPackages() ],
  },

If it generated the exact same code as above.

Additional context For JS-monorepos, as you might know, it is such a standardized way of organizing code, through separating them in different directories under a package directory, that this (so I assume at least) would probably apply to many use cases. Here a few examples of projects adopting this organization:

https://github.com/facebook/react/tree/main/packages https://github.com/ueberdosis/tiptap/tree/develop/packages https://github.com/vuejs/core/tree/main/packages

I can also picture trying to put together a pull request myself, if the issue appears relevant to you.

Thanks

javierbrea commented 7 months ago

Hi @tomosterlund , thank you very much, I'm glad you like it! 😃

I'm not sure about adding that kind of util to the core of the plugin. Maybe you could publish it as a separated npm package, and we could add an "ecosystem" section to the docs and link it there, or something similar. This is mainly because there are infinite ways of organizing the code, even when using a monorepo, and recognizing each package as a different type only solves one level of boundaries, but many users may want to check boundaries at more fine grain.

Anyway, there is already a way to solve your problem by using the plugin's capture feature. You could define a single type for all packages, capture each package name, and then write rules using the package name instead of the type. For example:

settings: {
    'boundaries/elements': [
      {
        type: 'package',
        pattern: 'packages/*/*',
        capture: ["packageName"] // This will get the value of the first "*", and store it as "packageName" in captured values.
      },
    ],
  },
rules: {
 "boundaries/element-types": [
          2,
           {
                from: [["package", { packageName: "foo" }]],
                allow: [["package", { packageName: "bar" }]],
           },
   ]
}

Note that I have written the example just from memory, without testing it. So, I recommend you to read the documentation about using captured values in element matchers, and let me know if you have any problem configuring it this way.

tomosterlund commented 4 months ago

Sorry for forgetting to follow up on this.

Thanks a lot! Makes sense if it doesn't fit into the lib, I'll try your suggestion out :)