Change the rules and the tools
This page shows how a project changes its architecture rules, and the settings of its tools: ESLint, Vite and Vitest, Playwright, Stryker and TypeScript. CAITS gives the defaults; your files have the last word.
The two kinds of files
| Files | Owner | What they hold |
|---|---|---|
quality/caits/* | CAITS | The defaults of CAITS. npx caits update writes them again: do not change them. |
quality/*, tsconfig.json | The project | The configuration of the project. Each file imports the defaults of CAITS, then changes them. CAITS never changes these files. |
Your file has the last word: what it removes, replaces or adds is what the tool uses.
The architecture rules
caits architecture checks the rules that quality/architecture.mjs exports. Each rule is an object with a name, a description, the kind of thing that it reads (on), and a check.
Remove a rule:
quality/architecture.mjs
import { rules } from "./caits/architecture.mjs";
export default rules.filter(rule => rule.name !== "routes-folders");Add a rule:
quality/architecture.mjs
import { rules } from "./caits/architecture.mjs";
const noLodashInDomain = {
name: "no-lodash-in-domain",
description: "The domain does not import lodash.",
on: "import",
check: ({ from, specifier }) => from.layer === "domain" && specifier.startsWith("lodash") && "the domain does not import lodash"
};
export default [...rules, noLodashInDomain];To replace a rule, remove it, then add your own rule with the same name.
A check gives false or undefined when the rule holds. Otherwise, it gives the message, or a list of messages. on tells what the check reads:
on | The check reads |
|---|---|
"import" | Each import of the production code: { file, from, to, specifier, internal, target }. |
"file" | Each file, and the project. With production: true, the check reads only the production code: the test files stay out. |
"project" | The project, once. The check gives a list of { file, message }. |
@ingenioz-it/caits/quality/architecture gives what the rules of CAITS use: locate, isCore, portOf, adapterOf, resolveImport… Read quality/caits/architecture.mjs for examples.
To test your rules, write quality/*.test.mjs with checkArchitecture(root, sources, rules) of the same module. npm test runs these tests.
ESLint
quality/eslint.config.js is a list. ESLint applies the entries in their order, so your entries win over those of CAITS:
quality/eslint.config.js
import caits from "./caits/eslint.js";
export default [
...caits,
{ files: ["src/**/*.ts"], rules: { "no-console": "error" } }
];Vite and Vitest
quality/vite.config.ts is the configuration of the build, of the development server and of the unit tests. mergeConfig adds your settings to those of CAITS:
quality/vite.config.ts
import { mergeConfig } from "vitest/config";
import caits from "./caits/vite.ts";
export default mergeConfig(caits, {
test: { coverage: { thresholds: { branches: 90 } } }
});mergeConfig adds to the lists (for example plugins and test.include). To replace a list, change it after the merge.
Playwright and Stryker
Your values replace those of CAITS. To change one value of an object, copy the others:
quality/playwright.config.ts
import { defineConfig } from "@playwright/test";
import caits from "./caits/playwright.ts";
export default defineConfig({
...caits,
use: { ...caits.use, locale: "fr-FR" }
});caits e2e reads webServer from quality/playwright.config.ts: if you change the port or the command of the server, the command uses your values.
Stryker asks for 100% of the mutants killed. To run it on fewer processes, for example on a small machine:
quality/stryker.config.mjs
import caits from "./caits/stryker.mjs";
export default {
...caits,
concurrency: 4
};To keep one mutant out, with its reason, see A mutant survived. Lower the thresholds only with your team: a test that checks nothing then has a place to hide.
TypeScript
tsconfig.json extends quality/caits/tsconfig.json. Add your options next to extends: they replace those of CAITS.
tsconfig.json
{
"extends": "./quality/caits/tsconfig.json",
"compilerOptions": { "noUnusedParameters": true }
}