Concepts: Kinds: A kind is a row name that one pattern gives to each file, such as entity for order.entity.ts. Kinds let one table cover many files with no declarations. # Kinds > A kind is a row name that one pattern gives to each file, such as entity for order.entity.ts. Kinds let one table cover many files with no declarations. Some codebases say what a file is in its name or its folder: `order.entity.ts`, `order.repository.ts`, `views/cart-page.tsx`. A **kind** turns that name into a table row. You write one pattern, and archangel gives each file its kind. You do not declare a part for each kind. ## The kinds pattern A table’s `kinds` option is a path pattern with the capture `{kind}`. The value of the capture is the file’s kind. | Pattern | File | Kind | | -------------------------- | ---------------------------------- | -------- | | `**/*.{kind}.ts` | `src/orders/order.entity.ts` | `entity` | | `src/features/*/{kind}/**` | `src/features/cart/views/page.tsx` | `views` | | `**/{kind}/**/*.{kind}.ts` | `src/entity/order.entity.ts` | `entity` | * `{kind}` can be in a pattern two times. Then the two places must give the same name. * A declaration file counts `.d.ts` as one extension. So `env.d.ts` has no kind `d`. * A file that the pattern does not match has no kind. ## A table of kinds This table uses the end of each file name as its kind. It also has one row, `shared`, that is a part. One table can mix kinds, parts and groups: archangel.config.ts ```ts import { defineConfig } from '@enhance-eng/archangel'; export default defineConfig({ parts: { shared: 'src/shared/**', }, rules: ({ mayImport }) => [ mayImport('orders', { service: ['repository', 'entity', 'shared'], repository: ['entity', 'shared'], entity: ['shared'], shared: [], }, { kinds: 'src/**/*.{kind}.ts', files: 'src/**', because: 'A service uses repositories. A repository stores entities. An entity knows only itself.', }), ], }); ``` `archangel explain` shows the kind of a file. The file is in no part, but it has the kind `service` in the table `orders`: Terminal ```ansi $ npx archangel explain src/orders/place-order.service.ts src/orders/place-order.service.ts part: no part; no file pattern matches it: "shared" src/shared/**: the path does not start with "src/shared/" kind: service in orders groups: none rules that apply: orders.service orders.service: service may import only repository, entity, shared, and itself (judges its imports) imports: line 1 './order.entity' -> src/orders/order.entity.ts line 2 './order.repository' -> src/orders/order.repository.ts line 3 './order.helpers' -> src/orders/order.helpers.ts breaks orders.service imported by: none ``` ## A new kind is not a silent gap The repository has a file that the table does not know: `order.helpers.ts`. Its kind is `helpers`, and the table has no row for it. Two rules find it: Terminal ```ansi $ npx archangel check error[every-file-has-a-kind]: src/orders/order.helpers.ts has the kind helpers, and the table "orders" has no row for it: no row for helpers. To fix this, add a row, rename the file, or leave it out of the table's files with `not` --> src/orders/order.helpers.ts = help: add a row, rename the file, or leave it out of the table's files with `not` = note: rule: orders.every-file-has-a-kind = note: because: A service uses repositories. A repository stores entities. An entity knows only itself. error[may-import]: service may not import helpers, through "./order.helpers": no row for helpers, and service may import only repository, entity, shared, and itself --> src/orders/place-order.service.ts:3:28 | 3 | import { orderTotal } from './order.helpers'; | ^^^^^^^^^^^^^^^^^ service may import only repository, entity, shared, and itself | = help: remove the import, or move the code to a file whose row permits the import = note: rule: orders.service = note: because: A service uses repositories. A repository stores entities. An entity knows only itself. checked 6 files and 7 imports: 2 problems For what a code means, run `archangel docs every-file-has-a-kind` or `archangel docs may-import`. ``` * `orders.every-file-has-a-kind` refuses the file itself. This rule checks only the files in the table’s `files`, here `src/**`. Without `files`, this rule checks nothing. * `orders.service` refuses the import of the file, because the row `service` does not list `helpers`, and the table has no row for it. To fix it, decide where helpers belong in your architecture. Add a row, and name it in the rows that may import it: archangel.config.ts ```ts import { defineConfig } from '@enhance-eng/archangel'; export default defineConfig({ parts: { shared: 'src/shared/**', }, rules: ({ mayImport }) => [ mayImport('orders', { service: ['repository', 'entity', 'helpers', 'shared'], repository: ['entity', 'shared'], entity: ['shared'], helpers: ['entity', 'shared'], shared: [], }, { kinds: 'src/**/*.{kind}.ts', files: 'src/**', because: 'A service uses repositories. A repository stores entities. An entity knows only itself.', }), ], }); ``` Terminal ```ansi $ npx archangel check checked 6 files and 7 imports: no problems ``` You can also rename the file so that it has a kind with a row, or leave it out of the table’s `files` with `not`. ## Kinds or parts | Use | When | | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | A kind | One pattern gives the names, for many folders or file endings. You add a kind when you add the first file with that ending, and the table tells you if it has no row. | | A part | The area has a name of its own, and rules other than this table use it. A rule’s `files` can name a part. | A kind has a meaning only in its table. A rule’s `files` cannot name a kind. To select the same files in a rule, write the pattern, for example `files: 'src/**/*.entity.ts'`. \`files\` does not change a kind The `files` option chooses only which importers the table judges, and which files must have a row. A file outside `files` still has its kind when another file imports it.