adamantite 0.41.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,92 @@
1
+ import { readPositiveIntegerOption } from "../options.js";
2
+ //#region presets/lint/plugin/rules/no-overzealous-destructuring.ts
3
+ /**
4
+ * `const { data: { user } } = query` nests two patterns, which stays allowed.
5
+ */
6
+ const DEFAULT_MAX_DEPTH = 2;
7
+ /**
8
+ * Five properties cover typical component props. A rest element does not count.
9
+ */
10
+ const DEFAULT_MAX_PROPERTIES = 5;
11
+ function isPattern(node) {
12
+ return node.type === "ObjectPattern" || node.type === "ArrayPattern";
13
+ }
14
+ /**
15
+ * The pattern that directly contains `pattern`, through a property, a default value, or a rest
16
+ * element. A root pattern, such as a declaration target or a parameter, has none.
17
+ */
18
+ function getParentPattern(pattern) {
19
+ let node = pattern.parent;
20
+ while (node.type === "AssignmentPattern" || node.type === "RestElement") node = node.parent;
21
+ if (node.type === "Property" && node.parent.type === "ObjectPattern") return node.parent;
22
+ return isPattern(node) ? node : void 0;
23
+ }
24
+ function getDepth(pattern) {
25
+ let depth = 1;
26
+ let parent = getParentPattern(pattern);
27
+ while (parent !== void 0) {
28
+ depth += 1;
29
+ parent = getParentPattern(parent);
30
+ }
31
+ return depth;
32
+ }
33
+ const rule = {
34
+ create(context) {
35
+ const maxDepth = readPositiveIntegerOption(context.options, "maxDepth") ?? DEFAULT_MAX_DEPTH;
36
+ const maxProperties = readPositiveIntegerOption(context.options, "maxProperties") ?? DEFAULT_MAX_PROPERTIES;
37
+ function checkDepth(node) {
38
+ const depth = getDepth(node);
39
+ if (depth === maxDepth + 1) context.report({
40
+ data: {
41
+ depth: String(depth),
42
+ maxDepth: String(maxDepth)
43
+ },
44
+ messageId: "tooDeep",
45
+ node
46
+ });
47
+ }
48
+ return {
49
+ ArrayPattern: checkDepth,
50
+ ObjectPattern(node) {
51
+ checkDepth(node);
52
+ const count = node.properties.filter((property) => property.type === "Property").length;
53
+ if (count > maxProperties) context.report({
54
+ data: {
55
+ count: String(count),
56
+ maxProperties: String(maxProperties)
57
+ },
58
+ messageId: "tooManyProperties",
59
+ node
60
+ });
61
+ }
62
+ };
63
+ },
64
+ meta: {
65
+ defaultOptions: [{
66
+ maxDepth: DEFAULT_MAX_DEPTH,
67
+ maxProperties: DEFAULT_MAX_PROPERTIES
68
+ }],
69
+ docs: { description: "Disallow destructuring patterns that nest too deep or take too many properties from one object." },
70
+ messages: {
71
+ tooDeep: "This destructuring pattern is nested {{depth}} levels deep. The limit is {{maxDepth}}. Keep the outer object and read its members, or split the pattern into separate declarations.",
72
+ tooManyProperties: "This destructuring pattern takes {{count}} properties from one object. The limit is {{maxProperties}}. Keep the object and read its members where you use them, or split the pattern."
73
+ },
74
+ schema: [{
75
+ additionalProperties: false,
76
+ properties: {
77
+ maxDepth: {
78
+ minimum: 1,
79
+ type: "integer"
80
+ },
81
+ maxProperties: {
82
+ minimum: 1,
83
+ type: "integer"
84
+ }
85
+ },
86
+ type: "object"
87
+ }],
88
+ type: "suggestion"
89
+ }
90
+ };
91
+ //#endregion
92
+ export { rule as default };
@@ -0,0 +1,10 @@
1
+ import { Rule } from "@oxlint/plugins";
2
+ //#region presets/lint/plugin/rules/no-react-state-hooks.d.ts
3
+ /**
4
+ * Convert a glob to a regular expression that matches a whole path with forward slashes. Supports
5
+ * `**`, `*`, `?`, `[a-z]` and `[!a-z]` character classes, and `{a,b}` alternation.
6
+ */
7
+ declare function globToRegExp(glob: string): RegExp;
8
+ declare const rule: Rule;
9
+ //#endregion
10
+ export { rule as default, globToRegExp };
@@ -0,0 +1,160 @@
1
+ import { readStringArrayOption } from "../options.js";
2
+ //#region presets/lint/plugin/rules/no-react-state-hooks.ts
3
+ const HOOK_MESSAGES = /* @__PURE__ */ new Map([
4
+ ["useCallback", "memoization"],
5
+ ["useEffect", "effect"],
6
+ ["useLayoutEffect", "effect"],
7
+ ["useMemo", "memoization"],
8
+ ["useReducer", "localState"],
9
+ ["useState", "localState"],
10
+ ["useSyncExternalStore", "externalStore"]
11
+ ]);
12
+ const DEFAULT_HOOKS = [...HOOK_MESSAGES.keys()];
13
+ /**
14
+ * Modules where hooks are permitted: hook modules such as `useCart.ts` or `use-cart.ts`, and
15
+ * everything under a `hooks` directory. Everything else is feature code, including files such as
16
+ * `userProfile.tsx`.
17
+ */
18
+ const DEFAULT_ALLOW = [
19
+ "**/use[A-Z]*.{ts,tsx}",
20
+ "**/use-*.{ts,tsx}",
21
+ "**/hooks/**"
22
+ ];
23
+ const REACT_MODULE = "react";
24
+ function escapeRegExp(text) {
25
+ return text.replaceAll(/[$()*+.?[\\\]^{|}]/g, String.raw`\$&`);
26
+ }
27
+ /**
28
+ * Convert a glob to a regular expression that matches a whole path with forward slashes. Supports
29
+ * `**`, `*`, `?`, `[a-z]` and `[!a-z]` character classes, and `{a,b}` alternation.
30
+ */
31
+ function globToRegExp(glob) {
32
+ let source = "";
33
+ let index = 0;
34
+ while (index < glob.length) {
35
+ const rest = glob.slice(index);
36
+ if (rest.startsWith("**/")) {
37
+ source += "(?:.*/)?";
38
+ index += 3;
39
+ } else if (rest === "/**") {
40
+ source += "(?:/.*)?";
41
+ index += 3;
42
+ } else if (rest.startsWith("**")) {
43
+ source += ".*";
44
+ index += 2;
45
+ } else if (rest.startsWith("*")) {
46
+ source += "[^/]*";
47
+ index += 1;
48
+ } else if (rest.startsWith("?")) {
49
+ source += "[^/]";
50
+ index += 1;
51
+ } else if (rest.startsWith("[") && rest.indexOf("]") > 1) {
52
+ const end = rest.indexOf("]");
53
+ const negated = rest.startsWith("[!");
54
+ const characters = rest.slice(negated ? 2 : 1, end).replaceAll(/[\\^]/g, String.raw`\$&`);
55
+ source += `[${negated ? "^" : ""}${characters}]`;
56
+ index += end + 1;
57
+ } else if (rest.startsWith("{") && rest.includes("}")) {
58
+ const end = rest.indexOf("}");
59
+ const alternatives = rest.slice(1, end).split(",").map((alternative) => escapeRegExp(alternative));
60
+ source += `(?:${alternatives.join("|")})`;
61
+ index += end + 1;
62
+ } else {
63
+ source += escapeRegExp(glob.charAt(index));
64
+ index += 1;
65
+ }
66
+ }
67
+ return new RegExp(`^${source}$`);
68
+ }
69
+ function toPosixPath(path) {
70
+ return path.replaceAll("\\", "/");
71
+ }
72
+ /**
73
+ * The path that `allow` globs match: relative to the working directory when the file is inside it,
74
+ * and absolute otherwise.
75
+ */
76
+ function getMatchPath(context) {
77
+ const filename = toPosixPath(context.filename);
78
+ const cwd = toPosixPath(context.cwd).replace(/\/$/, "");
79
+ return filename.startsWith(`${cwd}/`) ? filename.slice(cwd.length + 1) : filename;
80
+ }
81
+ function getImportedName(specifier) {
82
+ return specifier.imported.type === "Literal" ? specifier.imported.value : specifier.imported.name;
83
+ }
84
+ function isStringLiteral(node) {
85
+ return node.type === "Literal" && typeof node.value === "string";
86
+ }
87
+ function getPropertyName(member) {
88
+ if (!member.computed) return member.property.type === "Identifier" ? member.property.name : void 0;
89
+ return isStringLiteral(member.property) ? member.property.value : void 0;
90
+ }
91
+ function isCallee(node) {
92
+ const { parent } = node;
93
+ return parent?.type === "CallExpression" && parent.callee === node;
94
+ }
95
+ const rule = {
96
+ create(context) {
97
+ const hooks = new Set(readStringArrayOption(context.options, "hooks") ?? DEFAULT_HOOKS);
98
+ const allow = (readStringArrayOption(context.options, "allow") ?? DEFAULT_ALLOW).map((glob) => globToRegExp(glob));
99
+ const matchPath = getMatchPath(context);
100
+ if (allow.some((pattern) => pattern.test(matchPath))) return {};
101
+ function report(node, hook) {
102
+ context.report({
103
+ data: { hook },
104
+ messageId: HOOK_MESSAGES.get(hook) ?? "banned",
105
+ node
106
+ });
107
+ }
108
+ function getReferences(specifier) {
109
+ return context.sourceCode.getDeclaredVariables(specifier).flatMap((variable) => variable.references).map((reference) => reference.identifier);
110
+ }
111
+ return { ImportDeclaration(node) {
112
+ if (node.source.value !== REACT_MODULE || node.importKind === "type") return;
113
+ for (const specifier of node.specifiers) {
114
+ if (specifier.type === "ImportSpecifier") {
115
+ const hook = getImportedName(specifier);
116
+ if (specifier.importKind === "type" || !hooks.has(hook)) continue;
117
+ for (const identifier of getReferences(specifier)) if (isCallee(identifier)) report(identifier.parent, hook);
118
+ continue;
119
+ }
120
+ for (const identifier of getReferences(specifier)) {
121
+ const member = identifier.parent;
122
+ if (member.type !== "MemberExpression" || member.object !== identifier) continue;
123
+ const hook = getPropertyName(member);
124
+ if (hook !== void 0 && hooks.has(hook) && isCallee(member)) report(member.parent, hook);
125
+ }
126
+ }
127
+ } };
128
+ },
129
+ meta: {
130
+ defaultOptions: [{
131
+ allow: [...DEFAULT_ALLOW],
132
+ hooks: [...DEFAULT_HOOKS]
133
+ }],
134
+ docs: { description: "Disallow React state, effect, and memoization hooks in feature code. Hooks stay allowed in hook modules." },
135
+ messages: {
136
+ banned: "`{{hook}}` is not allowed in feature code. Move it into a hook module.",
137
+ effect: "`{{hook}}` runs a side effect in feature code. Move the side effect to a loader, an action, or an event handler.",
138
+ externalStore: "`{{hook}}` subscribes to an external store in feature code. Subscribe through a query or a store abstraction instead.",
139
+ localState: "`{{hook}}` keeps local state in feature code. Derive the value from loader data or URL params instead.",
140
+ memoization: "`{{hook}}` memoizes by hand. Remove it: the React Compiler handles memoization."
141
+ },
142
+ schema: [{
143
+ additionalProperties: false,
144
+ properties: {
145
+ allow: {
146
+ items: { type: "string" },
147
+ type: "array"
148
+ },
149
+ hooks: {
150
+ items: { type: "string" },
151
+ type: "array"
152
+ }
153
+ },
154
+ type: "object"
155
+ }],
156
+ type: "suggestion"
157
+ }
158
+ };
159
+ //#endregion
160
+ export { rule as default, globToRegExp };
@@ -0,0 +1,5 @@
1
+ import { OxlintConfig } from "oxlint";
2
+ //#region presets/lint/react-doctor.d.ts
3
+ declare const config: OxlintConfig;
4
+ //#endregion
5
+ export { config as default };
@@ -0,0 +1,20 @@
1
+ //#region presets/lint/react-doctor.ts
2
+ const config = {
3
+ jsPlugins: ["oxlint-plugin-react-doctor"],
4
+ rules: {
5
+ "react-doctor/no-create-store-in-render": "error",
6
+ "react-doctor/no-direct-state-mutation": "error",
7
+ "react-doctor/no-eager-new-in-use-state-initializer": "error",
8
+ "react-doctor/no-impure-state-updater": "error",
9
+ "react-doctor/no-mutating-reducer-state": "error",
10
+ "react-doctor/effect-observer-needs-disconnect": "error",
11
+ "react-doctor/effect-raf-loop-needs-cancel": "error",
12
+ "react-doctor/no-effect-event-handler": "error",
13
+ "react-doctor/no-event-trigger-state": "error",
14
+ "react-doctor/no-fetch-in-effect": "error",
15
+ "react-doctor/no-pass-live-state-to-parent": "error",
16
+ "react-doctor/no-set-state-after-await-in-effect": "error"
17
+ }
18
+ };
19
+ //#endregion
20
+ export { config as default };
@@ -0,0 +1,5 @@
1
+ import { OxlintConfig } from "oxlint";
2
+ //#region presets/lint/react-strict.d.ts
3
+ declare const config: OxlintConfig;
4
+ //#endregion
5
+ export { config as default };
@@ -0,0 +1,11 @@
1
+ //#region presets/lint/react-strict.ts
2
+ const PLUGIN_EXTENSION = import.meta.url.endsWith(".ts") ? "ts" : "js";
3
+ const config = {
4
+ jsPlugins: [{
5
+ name: "adamantite",
6
+ specifier: new URL(`plugin/index.${PLUGIN_EXTENSION}`, import.meta.url).href
7
+ }],
8
+ rules: { "adamantite/no-react-state-hooks": "error" }
9
+ };
10
+ //#endregion
11
+ export { config as default };
@@ -0,0 +1,5 @@
1
+ import { OxlintConfig } from "oxlint";
2
+ //#region presets/lint/shadcn.d.ts
3
+ declare const config: OxlintConfig;
4
+ //#endregion
5
+ export { config as default };
@@ -0,0 +1,22 @@
1
+ //#region presets/lint/shadcn.ts
2
+ const config = {
3
+ jsPlugins: ["@shadcn/lint"],
4
+ overrides: [{
5
+ files: ["**/components/ui/**"],
6
+ rules: {
7
+ "shadcn/no-arbitrary-values": "off",
8
+ "shadcn/no-restyle": "off",
9
+ "shadcn/require-static-classes": "off"
10
+ }
11
+ }],
12
+ rules: {
13
+ "shadcn/no-arbitrary-values": ["error", { allow: ["layout"] }],
14
+ "shadcn/no-inline-styles": "error",
15
+ "shadcn/no-raw-colors": "error",
16
+ "shadcn/no-restyle": ["error", { allow: ["layout"] }],
17
+ "shadcn/no-unknown-classes": "error",
18
+ "shadcn/require-static-classes": "error"
19
+ }
20
+ };
21
+ //#endregion
22
+ export { config as default };
@@ -0,0 +1,5 @@
1
+ import { OxlintConfig } from "oxlint";
2
+ //#region presets/lint/strict.d.ts
3
+ declare const config: OxlintConfig;
4
+ //#endregion
5
+ export { config as default };
@@ -0,0 +1,18 @@
1
+ //#region presets/lint/strict.ts
2
+ const PLUGIN_EXTENSION = import.meta.url.endsWith(".ts") ? "ts" : "js";
3
+ const config = {
4
+ jsPlugins: [{
5
+ name: "adamantite",
6
+ specifier: new URL(`plugin/index.${PLUGIN_EXTENSION}`, import.meta.url).href
7
+ }],
8
+ overrides: [{
9
+ files: ["**/*.{test,spec}.{ts,tsx,mts,cts}", "**/__tests__/**"],
10
+ rules: { "typescript/consistent-type-assertions": "error" }
11
+ }],
12
+ rules: {
13
+ "adamantite/no-overzealous-destructuring": "error",
14
+ "typescript/consistent-type-assertions": ["error", { assertionStyle: "never" }]
15
+ }
16
+ };
17
+ //#endregion
18
+ export { config as default };
@@ -1,11 +1,27 @@
1
1
  // Generated by scripts/vendor-plugins.ts — do not edit by hand.
2
2
  interface VendoredPluginRule {
3
- readonly create: (context: never) => Record<string, never>
3
+ readonly create: (context: object) => Record<string, never>
4
4
  }
5
5
 
6
6
  declare const vendoredPlugin: {
7
7
  readonly meta: { readonly name: string }
8
- readonly rules: { readonly [ruleName: string]: VendoredPluginRule }
8
+ readonly rules: {
9
+ readonly "no-chained-type-assertions": VendoredPluginRule
10
+ readonly "no-conditional-empty-object-spread": VendoredPluginRule
11
+ readonly "no-known-value-widening": VendoredPluginRule
12
+ readonly "no-module-mocking": VendoredPluginRule
13
+ readonly "no-object-parameters": VendoredPluginRule
14
+ readonly "no-reflect-apply": VendoredPluginRule
15
+ readonly "no-reflect-get": VendoredPluginRule
16
+ readonly "no-runtime-typeof": VendoredPluginRule
17
+ readonly "no-shape-in-symbol-names": VendoredPluginRule
18
+ readonly "no-unknown-parameters": VendoredPluginRule
19
+ readonly "no-unknown-returns": VendoredPluginRule
20
+ readonly "no-unknown-type-aliases": VendoredPluginRule
21
+ readonly "no-unsafe-dictionary-type": VendoredPluginRule
22
+ readonly "no-widen-then-assert": VendoredPluginRule
23
+ readonly "require-safety-comment-for-type-assertion": VendoredPluginRule
24
+ }
9
25
  }
10
26
 
11
27
  export default vendoredPlugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adamantite",
3
- "version": "0.41.0",
3
+ "version": "0.42.0",
4
4
  "description": "An strict and opinionated set of presets for modern TypeScript applications",
5
5
  "keywords": [
6
6
  "adamantite",
@@ -58,18 +58,19 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "@clack/prompts": "1.7.0",
61
- "@effect/platform-node": "4.0.0-rc.115",
61
+ "@effect/platform-node": "4.0.0",
62
62
  "defu": "6.1.7",
63
- "effect": "4.0.0-rc.115",
64
- "esrap": "2.3.6",
63
+ "effect": "4.0.0",
65
64
  "jsonc-parser": "3.3.1",
66
65
  "nypm": "0.6.9",
67
- "oxc-parser": "^0.147.0"
66
+ "oxc-parser": "0.150.0"
68
67
  },
69
68
  "devDependencies": {
70
69
  "@changesets/changelog-github": "1.0.0",
71
70
  "@changesets/cli": "3.0.1",
72
- "@effect/vitest": "4.0.0-rc.115",
71
+ "@effect/vitest": "4.0.0",
72
+ "@oxlint/plugins": "1.83.0",
73
+ "@shadcn/lint": "0.1.5",
73
74
  "@tanstack/intent": "0.3.7",
74
75
  "@types/figlet": "1.7.0",
75
76
  "@types/node": "22.20.1",
@@ -79,6 +80,7 @@
79
80
  "knip": "6.32.2",
80
81
  "oxfmt": "0.65.0",
81
82
  "oxlint": "1.83.0",
83
+ "oxlint-plugin-react-doctor": "0.9.14",
82
84
  "oxlint-tsgolint": "7.0.2001",
83
85
  "sherif": "1.13.0",
84
86
  "tsdown": "0.22.14",
@@ -107,7 +109,9 @@
107
109
  "test:build": "node scripts/test-build.ts",
108
110
  "test:coverage": "vitest run --coverage",
109
111
  "test:dist": "pnpm run build && pnpm run test:build && pnpm run test:smoke",
112
+ "test:presets": "vitest run --project presets",
110
113
  "test:smoke": "node scripts/test-smoke.ts",
114
+ "test:unit": "vitest run --project unit",
111
115
  "test:watch": "vitest",
112
116
  "version": "changeset version"
113
117
  }
@@ -28,12 +28,43 @@ npx adamantite init --non-interactive --script check --script fix --typescript -
28
28
  Repeat `--script`, `--preset`, and `--editor` for multiple values. Available values are:
29
29
 
30
30
  - Scripts: `check`, `fix`, `analyze`. In a detected monorepo, `analyze` also installs Sherif.
31
- The legacy `check:monorepo` and `fix:monorepo` values are rejected.
32
- - Presets: `react`, `nextjs`, `vue`, `jest`, `vitest`, `node`, `antislop`; editors:
33
- `vscode`, `zed`
31
+ - Presets: `react`, `react-strict`, `react-doctor`, `nextjs`, `vue`, `jest`, `vitest`, `node`,
32
+ `strict`, `antislop`, `shadcn`;
33
+ editors: `vscode`, `zed`
34
34
  - Optional flags: `--typescript`, `--install-extensions`, `--github-actions`, `--agents`,
35
35
  `--overwrite-scripts`
36
36
 
37
+ The `react-strict` preset needs no extra package. Its `adamantite/no-react-state-hooks` rule
38
+ reports React state, effect, and memoization hooks outside hook modules (`useCart.ts`,
39
+ `use-cart.ts`, and files under `hooks/`), and each message tells what to do instead. Do not move a hook call into
40
+ a hook module only to silence the rule: follow the message. Set the rule's `allow` option
41
+ when the project keeps hooks elsewhere.
42
+
43
+ The `strict` preset needs no extra package either. Its `adamantite/no-overzealous-destructuring`
44
+ rule reports destructuring nested more than 2 levels deep or object patterns with more than 5
45
+ properties; keep the object and read its members, or split the pattern. Do not raise
46
+ `maxDepth` or `maxProperties` only to silence a report. The preset also bans type assertions
47
+ outside tests.
48
+
49
+ The `react-doctor` preset requires the `react` preset and the `oxlint-plugin-react-doctor`
50
+ package, which `init` installs at a pinned version and doctor and `update` keep in sync. It
51
+ enables curated React Doctor rules for React state and effect misuse, such as
52
+ `no-effect-event-handler` and `no-fetch-in-effect`, and adds to `react-strict`. The package has
53
+ a modified MIT license: using it as machine learning training data or selling it as a
54
+ hosted product needs written permission from the copyright holder. Tell the user before you
55
+ select the preset.
56
+
57
+ The `shadcn` preset needs Tailwind v4 and the `@shadcn/lint` package, which `init` installs
58
+ at a pinned version and doctor and `update` keep in sync. The plugin finds components and
59
+ the theme through `components.json`. Without that file it looks in `components/ui` or
60
+ `src/components/ui` and discovers the stylesheet that imports Tailwind; when components
61
+ live elsewhere, set `settings.shadcn` (for example `{ ui: "@/ds" }`) in `oxlint.config.ts`.
62
+ The preset turns off the component-owned rules under `**/components/ui/**`; add the same
63
+ override for another component directory.
64
+
65
+ Adamantite requires TypeScript 7 or later as a dependency in the project's own
66
+ `package.json`. Without that entry, npm cannot add or update a managed plugin.
67
+
37
68
  Only select options supported by the project. Presets and TypeScript require `check` or `fix`;
38
69
  extension installation requires an editor; `--github-actions` requires a CI-compatible script
39
70
  and a supported package manager (bun, deno, npm, pnpm, or yarn). Omitted boolean flags are
@@ -53,11 +84,10 @@ adamantite fix
53
84
  adamantite analyze
54
85
  ```
55
86
 
56
- - Use `check` for read-only formatting, lint, and type-error validation.
87
+ - Use `check` for read-only formatting, lint, and type-error validation. `check --only lint` or `check --only format`
88
+ runs one stage.
57
89
  - Use `fix` for automatic Oxlint fixes followed by Oxfmt formatting. Add `--suggested`, `--dangerous`, or `--all` only
58
- with explicit permission after reviewing their impact.
59
- - Do not use `format`. It is deprecated and the next release removes it. `fix` writes formatting changes and `check`
60
- verifies formatting for read-only CI checks.
90
+ with explicit permission after reviewing their impact. `fix --only lint` or `fix --only format` runs one stage.
61
91
  - Use `analyze` for unused dependencies, exports, and files. In a monorepo it first checks
62
92
  workspace dependency consistency with Sherif. `analyze --only monorepo` or
63
93
  `analyze --only unused` runs one stage. `analyze --fix` may remove files, so inspect
@@ -68,8 +98,6 @@ adamantite analyze
68
98
  use `analyze --only unused --fix` to skip Sherif.
69
99
  - `analyze` runs Sherif without flags. Put Sherif exceptions in the `sherif` field of the root
70
100
  `package.json` (camelCase CLI options, e.g. `"ignoreDependency": ["tailwindcss"]`).
71
- - Do not use `monorepo`. It is deprecated and the next release removes it. Use `analyze`, or
72
- `analyze --only monorepo` to run only Sherif.
73
101
 
74
102
  To pass arguments to Knip, Oxlint, or Sherif, place them after `--`. For `check` and `fix` they
75
103
  go to Oxlint. For `analyze` they go to Knip, or to the stage that `--only` selects, so a Sherif