From 667a211eb14d895e40eb34652743649fd2de1914 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Mon, 2 Sep 2024 15:02:15 +0300 Subject: [PATCH 01/23] Add info about the new config structure, add a migration guide --- MIGRATION_GUIDE.md | 47 +++++++++ README.md | 207 +++++++++++++++++++++++++++++++++--- packages/steiger/README.md | 209 ++++++++++++++++++++++++++++++++++--- 3 files changed, 437 insertions(+), 26 deletions(-) create mode 100644 MIGRATION_GUIDE.md diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md new file mode 100644 index 00000000..105bf176 --- /dev/null +++ b/MIGRATION_GUIDE.md @@ -0,0 +1,47 @@ +# Migration guide + +## From 0.4.0 to 0.5.0 + +**Step 1**: First of all you need to make sure you upgraded Steiger package to version 0.5.0 or higher. + +**Step 2**: You need to install `@feature-sliced/steiger-plugin` package (that contains all FSD rules to run checks in your project). Run one of the following commands based on the package manager you use. + +**pnpm**: + +```shell +pnpm add @feature-sliced/steiger-plugin +``` + +**yarn**: + +```shell +yarn add @feature-sliced/steiger-plugin +``` + +**npm**: + +```shell +npm i @feature-sliced/steiger-plugin +``` + +**Step 3**: The final step. You need to bring your `steiger.config.js` file to the new configuration format. You can do that automatically by running one of the following commands. + +**! Don't forget to check the changes after the migration and bring them to the code style adopted in your project** + +**pnpm**: + +```shell +pnpx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/codemod/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js +``` + +_yarn_: + +```shell +yarn dlx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/codemod/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js +``` + +_npm_: + +```shell +npx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/codemod/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js +``` diff --git a/README.md b/README.md index 30898862..7229c294 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,22 @@ Universal file structure and project architecture linter. > [!NOTE] > The project is in beta and in active development. Some APIs may change. -# Features +> [!NOTE] +> Version 0.5.0 introduces a new config file format. Please refer to the [Configuration](#configuration) section for more information and migration guide. + +## Features - Built-in set of rules to validate adherence to [Feature-Sliced Design](https://feature-sliced.design/) - Watch mode - Rule configurability -# Installation +## Installation ```bash npm i -D steiger ``` -# Usage +## Usage ```bash steiger ./src @@ -31,23 +34,201 @@ To run in watch mode, add `-w`/`--watch` to the command: steiger ./src --watch ``` -# Configuration +## Configuration Steiger is zero-config! If you don't want to disable certain rules, you can safely skip this section. Steiger is configurable via `cosmiconfig`. That means that you can create a `steiger.config.ts` or `steiger.config.js` file in the root of your project to configure the rules. Import `{ defineConfig } from "steiger"` to get autocompletion. -```ts -import { defineConfig } from 'steiger' +The config file shape is highly inspired by ESLint's config file, so if you have configured ESLint before, you'll find it easy to configure Steiger. + +### Structure and concepts + +There are 3 types of config objects that you can put in your config file. They follow 2 purposes: registration and configuration. + +- Plugin - allows you to register a plugin that provides rules to run. (We will consider this one in more detail in a later section) +- Config Object - allows you to configure the behaviour of rules provided by the plugins. It has the following shape: + ```text + { + "files"?: , + "ignores"?: , + "rules": { + : | [, ] + } + } + ``` + ```javascript + export default defineConfig({ + files: ['**/shared/**'], + ignores: ['**/shared/__mocks__/**'], + rules: { + 'fsd/no-public-api': 'off', + 'fsd/forbidden-imports': ['warn', { someOption: false }], + }, + }) + ``` +- Global ignore - allows you to disable all rules for a specific part of the file system. + ```text + { + ignores: + } + ``` + ```javascript + export default defineConfig([ + ...fsd.configs.recommended, + { + ignores: ['**/shared/__mocks__/**'], + }, + ]) + ``` + +Parts of the config object: + +- Severity - can be one of the following: "off", "warn", "error" +- GlobArray - string array with glob patterns to match files and folders in your project. +- RuleOptions - an object that contains specific rule options and can be passed to the rule to configure its behavior. + +### Examples + +Here are some rules on how configuration is processed: + +- Config objects are processed from top to bottom, so if there are multiple config object that match the same file for the same rule, the last one will be applied. +- You can set options for a rule once. When set, options are applied for the entire file system that is covered by Steiger. + +Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. + +#### Example 1. Default case -export default defineConfig({ - rules: { - 'public-api': 'off', +```javascript +// ./steiger.config.ts +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([...fsd.configs.recommended]) +``` + +#### Example 2. FSD with all rules enabled by default, but excluding a couple of folders + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + ignores: ['**/__mocks__/**'], + }, +]) +``` + +#### Example 3. FSD without certain rules. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + rules: { + 'fsd/no-processes': 'off', + 'fsd/no-public-api-sidestep': 'warn', + }, + }, + { + files: ['./src/shared/**'], + rules: { + 'fsd/public-api': 'off', + }, }, -}) +]) ``` -# Rules +#### Example 4. Disabling a rule for files in a specific folder and the folder itself. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + files: ['**/shared', '**/shared/**'], + rules: { + 'fsd/no-public-api': 'off', + }, + }, +]) +``` + +#### Example 5. Using ignores along with files. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + files: ['**/shared', '**/shared/**'], + ignores: ['**/shared/lib/**', '**/shared/ui/**'], + rules: { + 'fsd/no-public-api': 'off', // Disable the rule for the shared folder, but not for the lib and ui folders + }, + }, +]) +``` + +#### Example 6. Setting rule options. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + rules: { + 'fsd/no-public-api': ['warn', { someOptions: true }], + }, + }, + { + files: ['./src/shared/**'], + rules: { + 'fsd/no-public-api': ['error', { someOptions: false }], // Would throw an error as you can't override the options + }, + }, +]) +``` + +### Plugins + +One of the main features that came with 0.5.0 release is the ability to use plugins to extend Steiger with custom rules. To use a plugin, you need to install it (if it is an open-source one) or just import if you wrote it yourself in your project, and add it to the config. + +The plugin object has the following shape: + +```javascript +const customPlugin = { + meta: { + name: 'super-duper-architecture', + version: '1.0.0', + }, + ruleDefinitions: [ + { + name: 'sda/no-classes', + check: (root, options: { forSure: boolean } = { forSure: true }) => { + /* … */ + }, + }, + ], +} +``` + +### Migration from 0.4.0 + +Version 0.5.0 introduced a new config file format. Follow the [instructions](MIGRATION_GUIDE.md) to migrate your config file. + +## Rules Currently, Steiger is not extendable with more rules, though that will change in the near future. The built-in rules check for the project's adherence to [Feature-Sliced Design](https://feature-sliced.design/). @@ -78,12 +259,12 @@ Currently, Steiger is not extendable with more rules, though that will change in -# Contribution +## Contribution Feel free to report an issue or open a discussion. Ensure you read our [Code of Conduct](CODE_OF_CONDUCT.md) first though :) To get started with the codebase, see our [Contributing guide](CONTRIBUTING.md). -# Legal info +## Legal info Project licensed under [MIT License](LICENSE.md). [Here's what it means](https://choosealicense.com/licenses/mit/) diff --git a/packages/steiger/README.md b/packages/steiger/README.md index 28e71af7..7229c294 100644 --- a/packages/steiger/README.md +++ b/packages/steiger/README.md @@ -7,19 +7,22 @@ Universal file structure and project architecture linter. > [!NOTE] > The project is in beta and in active development. Some APIs may change. -# Features +> [!NOTE] +> Version 0.5.0 introduces a new config file format. Please refer to the [Configuration](#configuration) section for more information and migration guide. + +## Features - Built-in set of rules to validate adherence to [Feature-Sliced Design](https://feature-sliced.design/) - Watch mode - Rule configurability -# Installation +## Installation ```bash npm i -D steiger ``` -# Usage +## Usage ```bash steiger ./src @@ -31,21 +34,201 @@ To run in watch mode, add `-w`/`--watch` to the command: steiger ./src --watch ``` -# Configuration +## Configuration + +Steiger is zero-config! If you don't want to disable certain rules, you can safely skip this section. Steiger is configurable via `cosmiconfig`. That means that you can create a `steiger.config.ts` or `steiger.config.js` file in the root of your project to configure the rules. Import `{ defineConfig } from "steiger"` to get autocompletion. -```ts -import { defineConfig } from 'steiger' +The config file shape is highly inspired by ESLint's config file, so if you have configured ESLint before, you'll find it easy to configure Steiger. + +### Structure and concepts + +There are 3 types of config objects that you can put in your config file. They follow 2 purposes: registration and configuration. + +- Plugin - allows you to register a plugin that provides rules to run. (We will consider this one in more detail in a later section) +- Config Object - allows you to configure the behaviour of rules provided by the plugins. It has the following shape: + ```text + { + "files"?: , + "ignores"?: , + "rules": { + : | [, ] + } + } + ``` + ```javascript + export default defineConfig({ + files: ['**/shared/**'], + ignores: ['**/shared/__mocks__/**'], + rules: { + 'fsd/no-public-api': 'off', + 'fsd/forbidden-imports': ['warn', { someOption: false }], + }, + }) + ``` +- Global ignore - allows you to disable all rules for a specific part of the file system. + ```text + { + ignores: + } + ``` + ```javascript + export default defineConfig([ + ...fsd.configs.recommended, + { + ignores: ['**/shared/__mocks__/**'], + }, + ]) + ``` + +Parts of the config object: + +- Severity - can be one of the following: "off", "warn", "error" +- GlobArray - string array with glob patterns to match files and folders in your project. +- RuleOptions - an object that contains specific rule options and can be passed to the rule to configure its behavior. + +### Examples + +Here are some rules on how configuration is processed: + +- Config objects are processed from top to bottom, so if there are multiple config object that match the same file for the same rule, the last one will be applied. +- You can set options for a rule once. When set, options are applied for the entire file system that is covered by Steiger. + +Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. + +#### Example 1. Default case + +```javascript +// ./steiger.config.ts +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([...fsd.configs.recommended]) +``` + +#### Example 2. FSD with all rules enabled by default, but excluding a couple of folders + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + ignores: ['**/__mocks__/**'], + }, +]) +``` + +#### Example 3. FSD without certain rules. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + rules: { + 'fsd/no-processes': 'off', + 'fsd/no-public-api-sidestep': 'warn', + }, + }, + { + files: ['./src/shared/**'], + rules: { + 'fsd/public-api': 'off', + }, + }, +]) +``` + +#### Example 4. Disabling a rule for files in a specific folder and the folder itself. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + files: ['**/shared', '**/shared/**'], + rules: { + 'fsd/no-public-api': 'off', + }, + }, +]) +``` + +#### Example 5. Using ignores along with files. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + files: ['**/shared', '**/shared/**'], + ignores: ['**/shared/lib/**', '**/shared/ui/**'], + rules: { + 'fsd/no-public-api': 'off', // Disable the rule for the shared folder, but not for the lib and ui folders + }, + }, +]) +``` + +#### Example 6. Setting rule options. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + rules: { + 'fsd/no-public-api': ['warn', { someOptions: true }], + }, + }, + { + files: ['./src/shared/**'], + rules: { + 'fsd/no-public-api': ['error', { someOptions: false }], // Would throw an error as you can't override the options + }, + }, +]) +``` + +### Plugins + +One of the main features that came with 0.5.0 release is the ability to use plugins to extend Steiger with custom rules. To use a plugin, you need to install it (if it is an open-source one) or just import if you wrote it yourself in your project, and add it to the config. -export default defineConfig({ - rules: { - 'no-public-api': 'off', +The plugin object has the following shape: + +```javascript +const customPlugin = { + meta: { + name: 'super-duper-architecture', + version: '1.0.0', }, -}) + ruleDefinitions: [ + { + name: 'sda/no-classes', + check: (root, options: { forSure: boolean } = { forSure: true }) => { + /* … */ + }, + }, + ], +} ``` -# Rules +### Migration from 0.4.0 + +Version 0.5.0 introduced a new config file format. Follow the [instructions](MIGRATION_GUIDE.md) to migrate your config file. + +## Rules Currently, Steiger is not extendable with more rules, though that will change in the near future. The built-in rules check for the project's adherence to [Feature-Sliced Design](https://feature-sliced.design/). @@ -76,12 +259,12 @@ Currently, Steiger is not extendable with more rules, though that will change in -# Contribution +## Contribution Feel free to report an issue or open a discussion. Ensure you read our [Code of Conduct](CODE_OF_CONDUCT.md) first though :) To get started with the codebase, see our [Contributing guide](CONTRIBUTING.md). -# Legal info +## Legal info Project licensed under [MIT License](LICENSE.md). [Here's what it means](https://choosealicense.com/licenses/mit/) From 18f240ed580511d316a8cefa5ae8a81ac7c60521 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Mon, 2 Sep 2024 15:03:59 +0300 Subject: [PATCH 02/23] Add a change log --- .changeset/little-zebras-pay.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/little-zebras-pay.md diff --git a/.changeset/little-zebras-pay.md b/.changeset/little-zebras-pay.md new file mode 100644 index 00000000..b8179e92 --- /dev/null +++ b/.changeset/little-zebras-pay.md @@ -0,0 +1,5 @@ +--- +'steiger': minor +--- + +Add documentation about the new config structure From a02bbf99db80d70f4cc8a41481d5d3d34b46b71d Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Tue, 3 Sep 2024 23:38:48 +0300 Subject: [PATCH 03/23] Fix the migration guide --- MIGRATION_GUIDE.md | 55 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 105bf176..ada3fc16 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -9,22 +9,63 @@ **pnpm**: ```shell -pnpm add @feature-sliced/steiger-plugin +pnpm add -D @feature-sliced/steiger-plugin ``` **yarn**: ```shell -yarn add @feature-sliced/steiger-plugin +yarn add -D @feature-sliced/steiger-plugin ``` **npm**: ```shell -npm i @feature-sliced/steiger-plugin +npm i -D @feature-sliced/steiger-plugin ``` -**Step 3**: The final step. You need to bring your `steiger.config.js` file to the new configuration format. You can do that automatically by running one of the following commands. +**Step 3**: The final step. You need to bring your `steiger.config.js` file to the new configuration format. You can do that automatically by running one of the following commands. (Alter the command before running if your config file has an extension different from `.js`) + +Here is an example of the transformation that will be applied to your config: + + + + +
BeforeAfter
+ +```ts +// steiger.config.ts +import { defineConfig } from 'steiger' + +export default defineConfig({ + rules: { + 'public-api': 'off', + 'ambiguous-slice-names': 'off', + 'nonexisting-rule': 'off', + }, +}) +``` + + + +```ts +// steiger.config.ts +import { defineConfig } from 'steiger' +import fsd from '@feature-sliced/steiger-plugin' + +export default defineConfig([ + ...fsd.configs.recommended, + { + rules: { + 'fsd/public-api': 'off', + 'fsd/ambiguous-slice-names': 'off', + 'nonexisting-rule': 'off', + }, + }, +]) +``` + +
**! Don't forget to check the changes after the migration and bring them to the code style adopted in your project** @@ -34,13 +75,13 @@ npm i @feature-sliced/steiger-plugin pnpx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/codemod/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js ``` -_yarn_: +**yarn**: ```shell -yarn dlx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/codemod/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js +yarn dlx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/codemod/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js ``` -_npm_: +**npm**: ```shell npx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/codemod/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js From c90b991bf55388f9f8174c35f9bed5cc06766976 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Wed, 4 Sep 2024 00:01:29 +0300 Subject: [PATCH 04/23] Remove excess parts from README --- EXAMPLES.md | 160 +++++++++++++++++++++++++++++++++++++ README.md | 160 +++---------------------------------- packages/steiger/README.md | 160 ++----------------------------------- 3 files changed, 177 insertions(+), 303 deletions(-) create mode 100644 EXAMPLES.md diff --git a/EXAMPLES.md b/EXAMPLES.md new file mode 100644 index 00000000..f618f1ff --- /dev/null +++ b/EXAMPLES.md @@ -0,0 +1,160 @@ +# Examples + +## Structure and concepts + +There are 3 types of config objects that you can put in your config file. They follow 2 purposes: registration and configuration. + +- Plugin - allows you to register a plugin that provides rules to run. (We will consider this one in more detail in a later section) +- Config Object - allows you to configure the behaviour of rules provided by the plugins. It has the following shape: + ```text + { + "files"?: , + "ignores"?: , + "rules": { + : | [, ] + } + } + ``` + ```javascript + export default defineConfig({ + files: ['**/shared/**'], + ignores: ['**/shared/__mocks__/**'], + rules: { + 'fsd/no-public-api': 'off', + 'fsd/forbidden-imports': ['warn', { someOption: false }], + }, + }) + ``` +- Global ignore - allows you to disable all rules for a specific part of the file system. + ```text + { + ignores: + } + ``` + ```javascript + export default defineConfig([ + ...fsd.configs.recommended, + { + ignores: ['**/shared/__mocks__/**'], + }, + ]) + ``` + +Parts of the config object: + +- Severity - can be one of the following: "off", "warn", "error" +- GlobArray - string array with glob patterns to match files and folders in your project. +- RuleOptions - an object that contains specific rule options and can be passed to the rule to configure its behavior. + +## Examples + +Here are some rules on how configuration is processed: + +- Config objects are processed from top to bottom, so if there are multiple config object that match the same file for the same rule, the last one will be applied. +- You can set options for a rule once. When set, options are applied for the entire file system that is covered by Steiger. + +Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. + +### Example 1. Default case + +```javascript +// ./steiger.config.ts +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([...fsd.configs.recommended]) +``` + +### Example 2. FSD with all rules enabled by default, but excluding a couple of folders + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + ignores: ['**/__mocks__/**'], + }, +]) +``` + +### Example 3. FSD without certain rules. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + rules: { + 'fsd/no-processes': 'off', + 'fsd/no-public-api-sidestep': 'warn', + }, + }, + { + files: ['./src/shared/**'], + rules: { + 'fsd/public-api': 'off', + }, + }, +]) +``` + +### Example 4. Disabling a rule for files in a specific folder and the folder itself. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + files: ['**/shared', '**/shared/**'], + rules: { + 'fsd/no-public-api': 'off', + }, + }, +]) +``` + +### Example 5. Using ignores along with files. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + files: ['**/shared', '**/shared/**'], + ignores: ['**/shared/lib/**', '**/shared/ui/**'], + rules: { + 'fsd/no-public-api': 'off', // Disable the rule for the shared folder, but not for the lib and ui folders + }, + }, +]) +``` + +### Example 6. Setting rule options. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + rules: { + 'fsd/no-public-api': ['warn', { someOptions: true }], + }, + }, + { + files: ['./src/shared/**'], + rules: { + 'fsd/no-public-api': ['error', { someOptions: false }], // Would throw an error as you can't override the options + }, + }, +]) +``` diff --git a/README.md b/README.md index 7229c294..a8bb2796 100644 --- a/README.md +++ b/README.md @@ -42,53 +42,7 @@ Steiger is configurable via `cosmiconfig`. That means that you can create a `ste The config file shape is highly inspired by ESLint's config file, so if you have configured ESLint before, you'll find it easy to configure Steiger. -### Structure and concepts - -There are 3 types of config objects that you can put in your config file. They follow 2 purposes: registration and configuration. - -- Plugin - allows you to register a plugin that provides rules to run. (We will consider this one in more detail in a later section) -- Config Object - allows you to configure the behaviour of rules provided by the plugins. It has the following shape: - ```text - { - "files"?: , - "ignores"?: , - "rules": { - : | [, ] - } - } - ``` - ```javascript - export default defineConfig({ - files: ['**/shared/**'], - ignores: ['**/shared/__mocks__/**'], - rules: { - 'fsd/no-public-api': 'off', - 'fsd/forbidden-imports': ['warn', { someOption: false }], - }, - }) - ``` -- Global ignore - allows you to disable all rules for a specific part of the file system. - ```text - { - ignores: - } - ``` - ```javascript - export default defineConfig([ - ...fsd.configs.recommended, - { - ignores: ['**/shared/__mocks__/**'], - }, - ]) - ``` - -Parts of the config object: - -- Severity - can be one of the following: "off", "warn", "error" -- GlobArray - string array with glob patterns to match files and folders in your project. -- RuleOptions - an object that contains specific rule options and can be passed to the rule to configure its behavior. - -### Examples +### Example Here are some rules on how configuration is processed: @@ -97,132 +51,36 @@ Here are some rules on how configuration is processed: Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. -#### Example 1. Default case - ```javascript // ./steiger.config.ts import fsd from '@feature-sliced/steiger-plugin' import defineConfig from 'steiger' -export default defineConfig([...fsd.configs.recommended]) -``` - -#### Example 2. FSD with all rules enabled by default, but excluding a couple of folders - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - export default defineConfig([ ...fsd.configs.recommended, { + // ignore all mock files for all rules ignores: ['**/__mocks__/**'], }, -]) -``` - -#### Example 3. FSD without certain rules. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - rules: { - 'fsd/no-processes': 'off', - 'fsd/no-public-api-sidestep': 'warn', - }, - }, { - files: ['./src/shared/**'], + files: ['**/shared/**'], rules: { + // disable public-api rule for files in /shared folder 'fsd/public-api': 'off', }, }, -]) -``` - -#### Example 4. Disabling a rule for files in a specific folder and the folder itself. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, { - files: ['**/shared', '**/shared/**'], + files: ['**/widgets/**'], + ignores: ['**/discount-offers/**'], rules: { - 'fsd/no-public-api': 'off', + // disable no-segmentless-slices rule for all widgets except /discount-offers + 'fsd/no-segmentless-slices': 'off', }, }, ]) ``` -#### Example 5. Using ignores along with files. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - files: ['**/shared', '**/shared/**'], - ignores: ['**/shared/lib/**', '**/shared/ui/**'], - rules: { - 'fsd/no-public-api': 'off', // Disable the rule for the shared folder, but not for the lib and ui folders - }, - }, -]) -``` - -#### Example 6. Setting rule options. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - rules: { - 'fsd/no-public-api': ['warn', { someOptions: true }], - }, - }, - { - files: ['./src/shared/**'], - rules: { - 'fsd/no-public-api': ['error', { someOptions: false }], // Would throw an error as you can't override the options - }, - }, -]) -``` - -### Plugins - -One of the main features that came with 0.5.0 release is the ability to use plugins to extend Steiger with custom rules. To use a plugin, you need to install it (if it is an open-source one) or just import if you wrote it yourself in your project, and add it to the config. - -The plugin object has the following shape: - -```javascript -const customPlugin = { - meta: { - name: 'super-duper-architecture', - version: '1.0.0', - }, - ruleDefinitions: [ - { - name: 'sda/no-classes', - check: (root, options: { forSure: boolean } = { forSure: true }) => { - /* … */ - }, - }, - ], -} -``` +For more details, see [examples](EXAMPLES.md). ### Migration from 0.4.0 diff --git a/packages/steiger/README.md b/packages/steiger/README.md index 7229c294..4830d1ad 100644 --- a/packages/steiger/README.md +++ b/packages/steiger/README.md @@ -42,53 +42,7 @@ Steiger is configurable via `cosmiconfig`. That means that you can create a `ste The config file shape is highly inspired by ESLint's config file, so if you have configured ESLint before, you'll find it easy to configure Steiger. -### Structure and concepts - -There are 3 types of config objects that you can put in your config file. They follow 2 purposes: registration and configuration. - -- Plugin - allows you to register a plugin that provides rules to run. (We will consider this one in more detail in a later section) -- Config Object - allows you to configure the behaviour of rules provided by the plugins. It has the following shape: - ```text - { - "files"?: , - "ignores"?: , - "rules": { - : | [, ] - } - } - ``` - ```javascript - export default defineConfig({ - files: ['**/shared/**'], - ignores: ['**/shared/__mocks__/**'], - rules: { - 'fsd/no-public-api': 'off', - 'fsd/forbidden-imports': ['warn', { someOption: false }], - }, - }) - ``` -- Global ignore - allows you to disable all rules for a specific part of the file system. - ```text - { - ignores: - } - ``` - ```javascript - export default defineConfig([ - ...fsd.configs.recommended, - { - ignores: ['**/shared/__mocks__/**'], - }, - ]) - ``` - -Parts of the config object: - -- Severity - can be one of the following: "off", "warn", "error" -- GlobArray - string array with glob patterns to match files and folders in your project. -- RuleOptions - an object that contains specific rule options and can be passed to the rule to configure its behavior. - -### Examples +### Example Here are some rules on how configuration is processed: @@ -97,133 +51,35 @@ Here are some rules on how configuration is processed: Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. -#### Example 1. Default case - ```javascript // ./steiger.config.ts import fsd from '@feature-sliced/steiger-plugin' import defineConfig from 'steiger' -export default defineConfig([...fsd.configs.recommended]) -``` - -#### Example 2. FSD with all rules enabled by default, but excluding a couple of folders - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - export default defineConfig([ ...fsd.configs.recommended, { + // ignore all mock files for all rules ignores: ['**/__mocks__/**'], }, -]) -``` - -#### Example 3. FSD without certain rules. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - rules: { - 'fsd/no-processes': 'off', - 'fsd/no-public-api-sidestep': 'warn', - }, - }, { - files: ['./src/shared/**'], + files: ['**/shared/**'], rules: { + // disable public-api rule for files in /shared folder 'fsd/public-api': 'off', }, }, -]) -``` - -#### Example 4. Disabling a rule for files in a specific folder and the folder itself. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - files: ['**/shared', '**/shared/**'], - rules: { - 'fsd/no-public-api': 'off', - }, - }, -]) -``` - -#### Example 5. Using ignores along with files. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - files: ['**/shared', '**/shared/**'], - ignores: ['**/shared/lib/**', '**/shared/ui/**'], - rules: { - 'fsd/no-public-api': 'off', // Disable the rule for the shared folder, but not for the lib and ui folders - }, - }, -]) -``` - -#### Example 6. Setting rule options. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - rules: { - 'fsd/no-public-api': ['warn', { someOptions: true }], - }, - }, { - files: ['./src/shared/**'], + files: ['**/widgets/**'], + ignores: ['**/discount-offers/**'], rules: { - 'fsd/no-public-api': ['error', { someOptions: false }], // Would throw an error as you can't override the options + // disable no-segmentless-slices rule for all widgets except /discount-offers + 'fsd/no-segmentless-slices': 'off', }, }, ]) ``` -### Plugins - -One of the main features that came with 0.5.0 release is the ability to use plugins to extend Steiger with custom rules. To use a plugin, you need to install it (if it is an open-source one) or just import if you wrote it yourself in your project, and add it to the config. - -The plugin object has the following shape: - -```javascript -const customPlugin = { - meta: { - name: 'super-duper-architecture', - version: '1.0.0', - }, - ruleDefinitions: [ - { - name: 'sda/no-classes', - check: (root, options: { forSure: boolean } = { forSure: true }) => { - /* … */ - }, - }, - ], -} -``` - ### Migration from 0.4.0 Version 0.5.0 introduced a new config file format. Follow the [instructions](MIGRATION_GUIDE.md) to migrate your config file. From f7ed06b75719b63d2670ed772db1caec8a7b2752 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Wed, 4 Sep 2024 01:17:01 +0300 Subject: [PATCH 05/23] Fix the migration guide --- MIGRATION_GUIDE.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index ada3fc16..2d7a50e1 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -41,7 +41,7 @@ export default defineConfig({ rules: { 'public-api': 'off', 'ambiguous-slice-names': 'off', - 'nonexisting-rule': 'off', + 'no-processes': 'off', }, }) ``` @@ -59,7 +59,7 @@ export default defineConfig([ rules: { 'fsd/public-api': 'off', 'fsd/ambiguous-slice-names': 'off', - 'nonexisting-rule': 'off', + 'fsd/no-processes': 'off', }, }, ]) @@ -72,17 +72,17 @@ export default defineConfig([ **pnpm**: ```shell -pnpx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/codemod/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js +pnpx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/master/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js ``` **yarn**: ```shell -yarn dlx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/codemod/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js +yarn dlx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/master/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js ``` **npm**: ```shell -npx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/codemod/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js +npx jscodeshift -t https://raw.githubusercontent.com/feature-sliced/steiger/master/packages/steiger/migrations/convert-config-to-flat.js steiger.config.js ``` From 813998ac3a50c96177cd3e8fe19a410d957e82b9 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Wed, 4 Sep 2024 01:22:35 +0300 Subject: [PATCH 06/23] Fix paths in examples --- EXAMPLES.md | 12 ++++++------ README.md | 4 ++-- packages/steiger/README.md | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index f618f1ff..f79bfec4 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -17,8 +17,8 @@ There are 3 types of config objects that you can put in your config file. They f ``` ```javascript export default defineConfig({ - files: ['**/shared/**'], - ignores: ['**/shared/__mocks__/**'], + files: ['./src/shared/**'], + ignores: ['./src/shared/__mocks__/**'], rules: { 'fsd/no-public-api': 'off', 'fsd/forbidden-imports': ['warn', { someOption: false }], @@ -35,7 +35,7 @@ There are 3 types of config objects that you can put in your config file. They f export default defineConfig([ ...fsd.configs.recommended, { - ignores: ['**/shared/__mocks__/**'], + ignores: ['./src/shared/__mocks__/**'], }, ]) ``` @@ -111,7 +111,7 @@ import defineConfig from 'steiger' export default defineConfig([ ...fsd.configs.recommended, { - files: ['**/shared', '**/shared/**'], + files: ['./src/shared', './src/shared/**'], rules: { 'fsd/no-public-api': 'off', }, @@ -128,8 +128,8 @@ import defineConfig from 'steiger' export default defineConfig([ ...fsd.configs.recommended, { - files: ['**/shared', '**/shared/**'], - ignores: ['**/shared/lib/**', '**/shared/ui/**'], + files: ['./src/shared', './src/shared/**'], + ignores: ['./src/shared/lib/**', './src/shared/ui/**'], rules: { 'fsd/no-public-api': 'off', // Disable the rule for the shared folder, but not for the lib and ui folders }, diff --git a/README.md b/README.md index a8bb2796..d69601cd 100644 --- a/README.md +++ b/README.md @@ -63,14 +63,14 @@ export default defineConfig([ ignores: ['**/__mocks__/**'], }, { - files: ['**/shared/**'], + files: ['./src/shared/**'], rules: { // disable public-api rule for files in /shared folder 'fsd/public-api': 'off', }, }, { - files: ['**/widgets/**'], + files: ['./src/widgets/**'], ignores: ['**/discount-offers/**'], rules: { // disable no-segmentless-slices rule for all widgets except /discount-offers diff --git a/packages/steiger/README.md b/packages/steiger/README.md index 4830d1ad..b64041dc 100644 --- a/packages/steiger/README.md +++ b/packages/steiger/README.md @@ -63,14 +63,14 @@ export default defineConfig([ ignores: ['**/__mocks__/**'], }, { - files: ['**/shared/**'], + files: ['./src/shared/**'], rules: { // disable public-api rule for files in /shared folder 'fsd/public-api': 'off', }, }, { - files: ['**/widgets/**'], + files: ['./src/widgets/**'], ignores: ['**/discount-offers/**'], rules: { // disable no-segmentless-slices rule for all widgets except /discount-offers From 6d3bbbd848619f312ae213889964b05637205efd Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Thu, 5 Sep 2024 23:11:32 +0300 Subject: [PATCH 07/23] Update README.md Co-authored-by: Lev Chelyadinov --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d69601cd..a775d14d 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ steiger ./src --watch Steiger is zero-config! If you don't want to disable certain rules, you can safely skip this section. -Steiger is configurable via `cosmiconfig`. That means that you can create a `steiger.config.ts` or `steiger.config.js` file in the root of your project to configure the rules. Import `{ defineConfig } from "steiger"` to get autocompletion. +Steiger is configurable via `cosmiconfig`. That means that you can create a `steiger.config.ts` or `steiger.config.js` file in the root of your project to configure the rules. The config file shape is highly inspired by ESLint's config file, so if you have configured ESLint before, you'll find it easy to configure Steiger. From 2f0228b32540a8d8880f8313ff63462c159987e3 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Thu, 5 Sep 2024 23:12:21 +0300 Subject: [PATCH 08/23] Update README.md Co-authored-by: Lev Chelyadinov --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a775d14d..7b0685fc 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ The config file shape is highly inspired by ESLint's config file, so if you have Here are some rules on how configuration is processed: -- Config objects are processed from top to bottom, so if there are multiple config object that match the same file for the same rule, the last one will be applied. +- Objects in the config array are processed in order, so later objects can override the objects before them. - You can set options for a rule once. When set, options are applied for the entire file system that is covered by Steiger. Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. From 4cfeef53727ffaa76f02a94ca86c21002fe0a3f1 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Thu, 5 Sep 2024 23:13:38 +0300 Subject: [PATCH 09/23] Update README.md Co-authored-by: Lev Chelyadinov --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b0685fc..ef93fdf3 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ The config file shape is highly inspired by ESLint's config file, so if you have Here are some rules on how configuration is processed: - Objects in the config array are processed in order, so later objects can override the objects before them. -- You can set options for a rule once. When set, options are applied for the entire file system that is covered by Steiger. +- If a rule has configurable options, then you can set it once in any of the objects in the array and these options will be applied to the entire codebase, regardless of `files` and `excludes`. Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. From 36af9c6f3cad80f308ba50dad49fe70da7464862 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Sat, 5 Oct 2024 14:44:05 +0300 Subject: [PATCH 10/23] Add info on global ignores and glob matching specifics --- packages/steiger/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/steiger/README.md b/packages/steiger/README.md index f8e538b7..3bb9d5e4 100644 --- a/packages/steiger/README.md +++ b/packages/steiger/README.md @@ -47,6 +47,7 @@ The config file shape is highly inspired by ESLint's config file, so if you have Here are some rules on how configuration is processed: - Config objects are processed from top to bottom, so if there are multiple config object that match the same file for the same rule, the last one will be applied. +- GlobalIgnore objects `{ ignores: ['**/__mocks__/**'] }` are applied to all rules, they are processed first and remove files from the linter sight forever, so you cannot re-assign severity for them in other later objects. - You can set options for a rule once. When set, options are applied for the entire file system that is covered by Steiger. Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. @@ -80,6 +81,18 @@ export default defineConfig([ ]) ``` +### Glob matching + +All globs are matched only against files, folder severities are computed based on the files inside them. The formula is simple: the folder severity is the highest severity of files inside it (from highest to lowest: error, warn, off). + +**Glob examples**: + +- `./src/shared/**` - matches all files in the `shared` folder and its subfolders +- `./src/shared/*` - matches all files that are direct children of the `shared` folder +- `./src/shared` - based on the fact that globs are matched against files, this one matches only `shared` file (without an extension) inside the `src` folder +- `**/__mocks__/**` - matches all files in all `__mocks__` folders throughout the project +- `**/*.(test|spec).ts` - matches all test files in the project + ### Migration from 0.4.0 Version 0.5.0 introduced a new config file format. Follow the [instructions](MIGRATION_GUIDE.md) to migrate your config file. From 83492303839a2ea36da3f32db396605dcd0a58bd Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Sat, 5 Oct 2024 14:50:56 +0300 Subject: [PATCH 11/23] Re-phrase some sentences --- packages/steiger/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/steiger/README.md b/packages/steiger/README.md index 3bb9d5e4..06f7fc91 100644 --- a/packages/steiger/README.md +++ b/packages/steiger/README.md @@ -47,7 +47,7 @@ The config file shape is highly inspired by ESLint's config file, so if you have Here are some rules on how configuration is processed: - Config objects are processed from top to bottom, so if there are multiple config object that match the same file for the same rule, the last one will be applied. -- GlobalIgnore objects `{ ignores: ['**/__mocks__/**'] }` are applied to all rules, they are processed first and remove files from the linter sight forever, so you cannot re-assign severity for them in other later objects. +- GlobalIgnore objects `{ ignores: ['**/__mocks__/**'] }` are applied to all rules, they are processed first and permanently remove files from the linter's field of view, so you can't reassign severity to them in other later objects. - You can set options for a rule once. When set, options are applied for the entire file system that is covered by Steiger. Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. From 28ac1e3e6c450631c41401b300624f73ef04764f Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Thu, 10 Oct 2024 19:11:41 +0300 Subject: [PATCH 12/23] Fix brace sets in glob examples --- packages/steiger/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/steiger/README.md b/packages/steiger/README.md index 06f7fc91..f5fdf9a4 100644 --- a/packages/steiger/README.md +++ b/packages/steiger/README.md @@ -91,7 +91,7 @@ All globs are matched only against files, folder severities are computed based o - `./src/shared/*` - matches all files that are direct children of the `shared` folder - `./src/shared` - based on the fact that globs are matched against files, this one matches only `shared` file (without an extension) inside the `src` folder - `**/__mocks__/**` - matches all files in all `__mocks__` folders throughout the project -- `**/*.(test|spec).ts` - matches all test files in the project +- `**/*.{test,spec}.{ts,tsx}` - matches all test files in the project ### Migration from 0.4.0 From 72000debf5ef42bc6a04a1fd87dc2962191d2f77 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Thu, 10 Oct 2024 19:12:29 +0300 Subject: [PATCH 13/23] Reflect changes in the main README --- README.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b30eb183..f5fdf9a4 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ npx steiger ./src --watch Steiger is zero-config! If you don't want to disable certain rules, you can safely skip this section. -Steiger is configurable via `cosmiconfig`. That means that you can create a `steiger.config.ts` or `steiger.config.js` file in the root of your project to configure the rules. +Steiger is configurable via `cosmiconfig`. That means that you can create a `steiger.config.ts` or `steiger.config.js` file in the root of your project to configure the rules. Import `{ defineConfig } from "steiger"` to get autocompletion. The config file shape is highly inspired by ESLint's config file, so if you have configured ESLint before, you'll find it easy to configure Steiger. @@ -46,8 +46,9 @@ The config file shape is highly inspired by ESLint's config file, so if you have Here are some rules on how configuration is processed: -- Objects in the config array are processed in order, so later objects can override the objects before them. -- If a rule has configurable options, then you can set it once in any of the objects in the array and these options will be applied to the entire codebase, regardless of `files` and `excludes`. +- Config objects are processed from top to bottom, so if there are multiple config object that match the same file for the same rule, the last one will be applied. +- GlobalIgnore objects `{ ignores: ['**/__mocks__/**'] }` are applied to all rules, they are processed first and permanently remove files from the linter's field of view, so you can't reassign severity to them in other later objects. +- You can set options for a rule once. When set, options are applied for the entire file system that is covered by Steiger. Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. @@ -80,7 +81,17 @@ export default defineConfig([ ]) ``` -For more details, see [examples](EXAMPLES.md). +### Glob matching + +All globs are matched only against files, folder severities are computed based on the files inside them. The formula is simple: the folder severity is the highest severity of files inside it (from highest to lowest: error, warn, off). + +**Glob examples**: + +- `./src/shared/**` - matches all files in the `shared` folder and its subfolders +- `./src/shared/*` - matches all files that are direct children of the `shared` folder +- `./src/shared` - based on the fact that globs are matched against files, this one matches only `shared` file (without an extension) inside the `src` folder +- `**/__mocks__/**` - matches all files in all `__mocks__` folders throughout the project +- `**/*.{test,spec}.{ts,tsx}` - matches all test files in the project ### Migration from 0.4.0 From cb3299f16c2fe0700dd8160b2d4df971f6caf30d Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Wed, 23 Oct 2024 01:29:51 +0300 Subject: [PATCH 14/23] Update README.md Co-authored-by: Lev Chelyadinov --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f5fdf9a4..c53eea8e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Universal file structure and project architecture linter. > The project is in beta and in active development. Some APIs may change. > [!NOTE] -> Version 0.5.0 introduces a new config file format. Please refer to the [Configuration](#configuration) section for more information and migration guide. +> Version 0.5.0 introduced a new config file format. We have a codemod to automatically update your config, see the [migration guide](./MIGRATION_GUIDE.md). ## Features From 8904f53b4dfc4c00975153624dafa9a9a8021db2 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Wed, 23 Oct 2024 01:35:19 +0300 Subject: [PATCH 15/23] Add a simple example to the beginning of the Example section --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index c53eea8e..213870e1 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,23 @@ The config file shape is highly inspired by ESLint's config file, so if you have ### Example +```javascript +// ./steiger.config.js +import { defineConfig } from 'steiger' +import fsd from '@feature-sliced/steiger-plugin' + +export default defineConfig([ + ...fsd.configs.recommended, + { + // disable the `public-api` rule for files in the Shared layer + files: ['./src/shared/**'], + rules: { + 'fsd/public-api': 'off', + }, + }, +]) +``` + Here are some rules on how configuration is processed: - Config objects are processed from top to bottom, so if there are multiple config object that match the same file for the same rule, the last one will be applied. From 62a03dafbe0d8f1ca1ee0693d6b5da032b29287a Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Wed, 23 Oct 2024 01:42:07 +0300 Subject: [PATCH 16/23] Simplify the Example section --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 213870e1..4b52c241 100644 --- a/README.md +++ b/README.md @@ -61,13 +61,14 @@ export default defineConfig([ ]) ``` -Here are some rules on how configuration is processed: - -- Config objects are processed from top to bottom, so if there are multiple config object that match the same file for the same rule, the last one will be applied. -- GlobalIgnore objects `{ ignores: ['**/__mocks__/**'] }` are applied to all rules, they are processed first and permanently remove files from the linter's field of view, so you can't reassign severity to them in other later objects. -- You can set options for a rule once. When set, options are applied for the entire file system that is covered by Steiger. - -Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. +> [!TIP] +> If you want Steiger to ignore certain files, add an object like this to the config array: +> +> ```js +> defineConfig([, /* … */ { ignores: ['**/__mocks__/**'] }]) +> ``` + +Here's an example of a more complex configuration: ```javascript // ./steiger.config.ts From 7bd6730ecacb7617276a1f244699639aab1e1a46 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Wed, 23 Oct 2024 01:46:14 +0300 Subject: [PATCH 17/23] Fix the complex config example and hide it under a spoiler --- README.md | 62 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 4b52c241..ce7ea9a1 100644 --- a/README.md +++ b/README.md @@ -68,36 +68,38 @@ export default defineConfig([ > defineConfig([, /* … */ { ignores: ['**/__mocks__/**'] }]) > ``` -Here's an example of a more complex configuration: - -```javascript -// ./steiger.config.ts -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - // ignore all mock files for all rules - ignores: ['**/__mocks__/**'], - }, - { - files: ['./src/shared/**'], - rules: { - // disable public-api rule for files in /shared folder - 'fsd/public-api': 'off', - }, - }, - { - files: ['./src/widgets/**'], - ignores: ['**/discount-offers/**'], - rules: { - // disable no-segmentless-slices rule for all widgets except /discount-offers - 'fsd/no-segmentless-slices': 'off', - }, - }, -]) -``` +
+ Comprehensive showcase of the config file syntax + + ```javascript + // ./steiger.config.ts + import { defineConfig } from 'steiger' + import fsd from '@feature-sliced/steiger-plugin' + + export default defineConfig([ + ...fsd.configs.recommended, + { + // ignore all mock files for all rules + ignores: ['**/__mocks__/**'], + }, + { + files: ['./src/shared/**'], + rules: { + // disable public-api rule for files in /shared folder + 'fsd/public-api': 'off', + }, + }, + { + files: ['./src/widgets/**'], + ignores: ['**/discount-offers/**'], + rules: { + // disable no-segmentless-slices rule for all widgets except /discount-offers + 'fsd/no-segmentless-slices': 'off', + }, + }, + ]) + ``` +
### Glob matching From 545766c4928d7da9a9ee42aa62d08b056a18ea1d Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Wed, 23 Oct 2024 01:50:45 +0300 Subject: [PATCH 18/23] Update MIGRATION_GUIDE.md Co-authored-by: Lev Chelyadinov --- MIGRATION_GUIDE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 2d7a50e1..56334ae1 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -67,7 +67,8 @@ export default defineConfig([ -**! Don't forget to check the changes after the migration and bring them to the code style adopted in your project** +> [!NOTE] +> The codemod will probably get the formatting wrong, so don't forget to check the file yourself after migration. **pnpm**: From b952267f74bd1cc9a943db8aa3b1c187dce3e356 Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Wed, 23 Oct 2024 01:58:33 +0300 Subject: [PATCH 19/23] Button up the docs --- CONFIG_EXAMPLES.md | 105 +++++++++++++++++++++++++++++ EXAMPLES.md | 160 --------------------------------------------- README.md | 1 + 3 files changed, 106 insertions(+), 160 deletions(-) create mode 100644 CONFIG_EXAMPLES.md delete mode 100644 EXAMPLES.md diff --git a/CONFIG_EXAMPLES.md b/CONFIG_EXAMPLES.md new file mode 100644 index 00000000..a1878156 --- /dev/null +++ b/CONFIG_EXAMPLES.md @@ -0,0 +1,105 @@ +# Examples + +## Example 1. Default case + +```javascript +// ./steiger.config.ts +import fsd from '@feature-sliced/steiger-plugin' +import defineConfig from 'steiger' + +export default defineConfig([...fsd.configs.recommended]) +``` + +## Example 2. FSD with all rules enabled by default, but excluding a couple of folders + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import { defineConfig } from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + ignores: ['**/__mocks__/**'], + }, +]) +``` + +## Example 3. FSD without certain rules. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import { defineConfig } from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + rules: { + 'fsd/no-processes': 'off', + 'fsd/no-public-api-sidestep': 'warn', + }, + }, + { + files: ['./src/shared/**'], + rules: { + 'fsd/public-api': 'off', + }, + }, +]) +``` + +## Example 4. Disabling a rule for files in a specific folder and the folder itself. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import { defineConfig } from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + files: ['./src/shared', './src/shared/**'], + rules: { + 'fsd/no-public-api': 'off', + }, + }, +]) +``` + +## Example 5. Using ignores along with files. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import { defineConfig } from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + files: ['./src/shared', './src/shared/**'], + ignores: ['./src/shared/lib/**', './src/shared/ui/**'], + rules: { + 'fsd/no-public-api': 'off', // Disable the rule for the shared folder, but not for the lib and ui folders + }, + }, +]) +``` + +## Example 6. Setting rule options. + +```javascript +import fsd from '@feature-sliced/steiger-plugin' +import { defineConfig } from 'steiger' + +export default defineConfig([ + ...fsd.configs.recommended, + { + rules: { + 'fsd/no-public-api': ['warn', { someOptions: true }], + }, + }, + { + files: ['./src/shared/**'], + rules: { + 'fsd/no-public-api': ['error', { someOptions: false }], // Would throw an error as you can't override the options + }, + }, +]) +``` diff --git a/EXAMPLES.md b/EXAMPLES.md deleted file mode 100644 index f79bfec4..00000000 --- a/EXAMPLES.md +++ /dev/null @@ -1,160 +0,0 @@ -# Examples - -## Structure and concepts - -There are 3 types of config objects that you can put in your config file. They follow 2 purposes: registration and configuration. - -- Plugin - allows you to register a plugin that provides rules to run. (We will consider this one in more detail in a later section) -- Config Object - allows you to configure the behaviour of rules provided by the plugins. It has the following shape: - ```text - { - "files"?: , - "ignores"?: , - "rules": { - : | [, ] - } - } - ``` - ```javascript - export default defineConfig({ - files: ['./src/shared/**'], - ignores: ['./src/shared/__mocks__/**'], - rules: { - 'fsd/no-public-api': 'off', - 'fsd/forbidden-imports': ['warn', { someOption: false }], - }, - }) - ``` -- Global ignore - allows you to disable all rules for a specific part of the file system. - ```text - { - ignores: - } - ``` - ```javascript - export default defineConfig([ - ...fsd.configs.recommended, - { - ignores: ['./src/shared/__mocks__/**'], - }, - ]) - ``` - -Parts of the config object: - -- Severity - can be one of the following: "off", "warn", "error" -- GlobArray - string array with glob patterns to match files and folders in your project. -- RuleOptions - an object that contains specific rule options and can be passed to the rule to configure its behavior. - -## Examples - -Here are some rules on how configuration is processed: - -- Config objects are processed from top to bottom, so if there are multiple config object that match the same file for the same rule, the last one will be applied. -- You can set options for a rule once. When set, options are applied for the entire file system that is covered by Steiger. - -Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. - -### Example 1. Default case - -```javascript -// ./steiger.config.ts -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([...fsd.configs.recommended]) -``` - -### Example 2. FSD with all rules enabled by default, but excluding a couple of folders - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - ignores: ['**/__mocks__/**'], - }, -]) -``` - -### Example 3. FSD without certain rules. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - rules: { - 'fsd/no-processes': 'off', - 'fsd/no-public-api-sidestep': 'warn', - }, - }, - { - files: ['./src/shared/**'], - rules: { - 'fsd/public-api': 'off', - }, - }, -]) -``` - -### Example 4. Disabling a rule for files in a specific folder and the folder itself. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - files: ['./src/shared', './src/shared/**'], - rules: { - 'fsd/no-public-api': 'off', - }, - }, -]) -``` - -### Example 5. Using ignores along with files. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - files: ['./src/shared', './src/shared/**'], - ignores: ['./src/shared/lib/**', './src/shared/ui/**'], - rules: { - 'fsd/no-public-api': 'off', // Disable the rule for the shared folder, but not for the lib and ui folders - }, - }, -]) -``` - -### Example 6. Setting rule options. - -```javascript -import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' - -export default defineConfig([ - ...fsd.configs.recommended, - { - rules: { - 'fsd/no-public-api': ['warn', { someOptions: true }], - }, - }, - { - files: ['./src/shared/**'], - rules: { - 'fsd/no-public-api': ['error', { someOptions: false }], // Would throw an error as you can't override the options - }, - }, -]) -``` diff --git a/README.md b/README.md index ce7ea9a1..75894630 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ export default defineConfig([ }, ]) ``` + [You can see more examples here](CONFIG_EXAMPLES.md) ### Glob matching From 35c187594277c7e9073c747269ca8f56261e271c Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Wed, 23 Oct 2024 01:59:32 +0300 Subject: [PATCH 20/23] Move the Glob matching section to config examples doc --- CONFIG_EXAMPLES.md | 12 ++++++++++++ README.md | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CONFIG_EXAMPLES.md b/CONFIG_EXAMPLES.md index a1878156..ffb0bf37 100644 --- a/CONFIG_EXAMPLES.md +++ b/CONFIG_EXAMPLES.md @@ -1,5 +1,17 @@ # Examples +## Glob matching + +All globs are matched only against files, folder severities are computed based on the files inside them. The formula is simple: the folder severity is the highest severity of files inside it (from highest to lowest: error, warn, off). + +**Glob examples**: + +- `./src/shared/**` - matches all files in the `shared` folder and its subfolders +- `./src/shared/*` - matches all files that are direct children of the `shared` folder +- `./src/shared` - based on the fact that globs are matched against files, this one matches only `shared` file (without an extension) inside the `src` folder +- `**/__mocks__/**` - matches all files in all `__mocks__` folders throughout the project +- `**/*.{test,spec}.{ts,tsx}` - matches all test files in the project + ## Example 1. Default case ```javascript diff --git a/README.md b/README.md index 75894630..167b74e7 100644 --- a/README.md +++ b/README.md @@ -102,18 +102,6 @@ export default defineConfig([ [You can see more examples here](CONFIG_EXAMPLES.md) -### Glob matching - -All globs are matched only against files, folder severities are computed based on the files inside them. The formula is simple: the folder severity is the highest severity of files inside it (from highest to lowest: error, warn, off). - -**Glob examples**: - -- `./src/shared/**` - matches all files in the `shared` folder and its subfolders -- `./src/shared/*` - matches all files that are direct children of the `shared` folder -- `./src/shared` - based on the fact that globs are matched against files, this one matches only `shared` file (without an extension) inside the `src` folder -- `**/__mocks__/**` - matches all files in all `__mocks__` folders throughout the project -- `**/*.{test,spec}.{ts,tsx}` - matches all test files in the project - ### Migration from 0.4.0 Version 0.5.0 introduced a new config file format. Follow the [instructions](MIGRATION_GUIDE.md) to migrate your config file. From 17da9b561b0957d6ac70ec2cfffa5c6e31bf4dda Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Wed, 23 Oct 2024 02:02:40 +0300 Subject: [PATCH 21/23] Reflect the changes in README.md inside packages/steiger --- packages/steiger/README.md | 53 +++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/packages/steiger/README.md b/packages/steiger/README.md index f5fdf9a4..656de3ca 100644 --- a/packages/steiger/README.md +++ b/packages/steiger/README.md @@ -8,7 +8,7 @@ Universal file structure and project architecture linter. > The project is in beta and in active development. Some APIs may change. > [!NOTE] -> Version 0.5.0 introduces a new config file format. Please refer to the [Configuration](#configuration) section for more information and migration guide. +> Version 0.5.0 introduced a new config file format. We have a codemod to automatically update your config, see the [migration guide](./MIGRATION_GUIDE.md). ## Features @@ -44,18 +44,37 @@ The config file shape is highly inspired by ESLint's config file, so if you have ### Example -Here are some rules on how configuration is processed: +```javascript +// ./steiger.config.js +import { defineConfig } from 'steiger' +import fsd from '@feature-sliced/steiger-plugin' -- Config objects are processed from top to bottom, so if there are multiple config object that match the same file for the same rule, the last one will be applied. -- GlobalIgnore objects `{ ignores: ['**/__mocks__/**'] }` are applied to all rules, they are processed first and permanently remove files from the linter's field of view, so you can't reassign severity to them in other later objects. -- You can set options for a rule once. When set, options are applied for the entire file system that is covered by Steiger. +export default defineConfig([ + ...fsd.configs.recommended, + { + // disable the `public-api` rule for files in the Shared layer + files: ['./src/shared/**'], + rules: { + 'fsd/public-api': 'off', + }, + }, +]) +``` -Note that this line `...fsd.configs.recommended,` just takes the plugin and the recommended rules configuration (all enabled with "error" severity by default) and puts it into the config array. +> [!TIP] +> If you want Steiger to ignore certain files, add an object like this to the config array: +> +> ```js +> defineConfig([, /* … */ { ignores: ['**/__mocks__/**'] }]) +> ``` + +
+ Comprehensive showcase of the config file syntax ```javascript // ./steiger.config.ts +import { defineConfig } from 'steiger' import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' export default defineConfig([ ...fsd.configs.recommended, @@ -81,21 +100,13 @@ export default defineConfig([ ]) ``` -### Glob matching - -All globs are matched only against files, folder severities are computed based on the files inside them. The formula is simple: the folder severity is the highest severity of files inside it (from highest to lowest: error, warn, off). - -**Glob examples**: +[You can see more examples here](../../CONFIG_EXAMPLES.md) -- `./src/shared/**` - matches all files in the `shared` folder and its subfolders -- `./src/shared/*` - matches all files that are direct children of the `shared` folder -- `./src/shared` - based on the fact that globs are matched against files, this one matches only `shared` file (without an extension) inside the `src` folder -- `**/__mocks__/**` - matches all files in all `__mocks__` folders throughout the project -- `**/*.{test,spec}.{ts,tsx}` - matches all test files in the project +
### Migration from 0.4.0 -Version 0.5.0 introduced a new config file format. Follow the [instructions](MIGRATION_GUIDE.md) to migrate your config file. +Version 0.5.0 introduced a new config file format. Follow the [instructions](../../MIGRATION_GUIDE.md) to migrate your config file. ## Rules @@ -132,10 +143,10 @@ Currently, Steiger is not extendable with more rules, though that will change in ## Contribution -Feel free to report an issue or open a discussion. Ensure you read our [Code of Conduct](CODE_OF_CONDUCT.md) first though :) +Feel free to report an issue or open a discussion. Ensure you read our [Code of Conduct](../../CODE_OF_CONDUCT.md) first though :) -To get started with the codebase, see our [Contributing guide](CONTRIBUTING.md). +To get started with the codebase, see our [Contributing guide](../../CONTRIBUTING.md). ## Legal info -Project licensed under [MIT License](LICENSE.md). [Here's what it means](https://choosealicense.com/licenses/mit/) +Project licensed under [MIT License](../../LICENSE.md). [Here's what it means](https://choosealicense.com/licenses/mit/) From e075e91dc4e18ffc511d4fdb08c3b50051f18f4e Mon Sep 17 00:00:00 2001 From: Daniil Sapa Date: Fri, 25 Oct 2024 01:27:51 +0300 Subject: [PATCH 22/23] Fix imports in the examples --- CONFIG_EXAMPLES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONFIG_EXAMPLES.md b/CONFIG_EXAMPLES.md index ffb0bf37..b103e58a 100644 --- a/CONFIG_EXAMPLES.md +++ b/CONFIG_EXAMPLES.md @@ -17,7 +17,7 @@ All globs are matched only against files, folder severities are computed based o ```javascript // ./steiger.config.ts import fsd from '@feature-sliced/steiger-plugin' -import defineConfig from 'steiger' +import { defineConfig } from 'steiger' export default defineConfig([...fsd.configs.recommended]) ``` From 898ae07e9501efbe96ccb35363d7c275bd50b3e4 Mon Sep 17 00:00:00 2001 From: Lev Chelyadinov Date: Fri, 25 Oct 2024 11:18:01 +0200 Subject: [PATCH 23/23] Make some minor fixes to examples --- CONFIG_EXAMPLES.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/CONFIG_EXAMPLES.md b/CONFIG_EXAMPLES.md index b103e58a..0186b897 100644 --- a/CONFIG_EXAMPLES.md +++ b/CONFIG_EXAMPLES.md @@ -36,7 +36,7 @@ export default defineConfig([ ]) ``` -## Example 3. FSD without certain rules. +## Example 3. FSD without certain rules ```javascript import fsd from '@feature-sliced/steiger-plugin' @@ -59,7 +59,7 @@ export default defineConfig([ ]) ``` -## Example 4. Disabling a rule for files in a specific folder and the folder itself. +## Example 4. Disabling a rule for a specific folder ```javascript import fsd from '@feature-sliced/steiger-plugin' @@ -68,7 +68,7 @@ import { defineConfig } from 'steiger' export default defineConfig([ ...fsd.configs.recommended, { - files: ['./src/shared', './src/shared/**'], + files: ['./src/shared/**'], rules: { 'fsd/no-public-api': 'off', }, @@ -76,7 +76,7 @@ export default defineConfig([ ]) ``` -## Example 5. Using ignores along with files. +## Example 5. Using ignores along with files ```javascript import fsd from '@feature-sliced/steiger-plugin' @@ -85,7 +85,7 @@ import { defineConfig } from 'steiger' export default defineConfig([ ...fsd.configs.recommended, { - files: ['./src/shared', './src/shared/**'], + files: ['./src/shared/**'], ignores: ['./src/shared/lib/**', './src/shared/ui/**'], rules: { 'fsd/no-public-api': 'off', // Disable the rule for the shared folder, but not for the lib and ui folders @@ -94,7 +94,7 @@ export default defineConfig([ ]) ``` -## Example 6. Setting rule options. +## Example 6. Setting rule options ```javascript import fsd from '@feature-sliced/steiger-plugin' @@ -110,7 +110,8 @@ export default defineConfig([ { files: ['./src/shared/**'], rules: { - 'fsd/no-public-api': ['error', { someOptions: false }], // Would throw an error as you can't override the options + 'fsd/no-public-api': 'error', + // 'fsd/no-public-api': ['error', { someOptions: false }], // Would throw an error as you can't override the options }, }, ])