adamantite 0.42.0 → 0.43.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.
- package/README.md +231 -4
- package/dist/index.js +51 -23
- package/dist/presets/analyze.d.ts +6 -2
- package/dist/presets/analyze.js +7 -1
- package/dist/presets/lint/core.d.ts +5 -3
- package/dist/presets/lint/core.js +1 -0
- package/dist/presets/lint/custom.d.ts +60 -0
- package/dist/presets/lint/custom.js +142 -0
- package/dist/presets/lint/effect.d.ts +5 -0
- package/dist/presets/lint/effect.js +106 -0
- package/dist/presets/lint/plugin/imports.d.ts +19 -0
- package/dist/presets/lint/plugin/imports.js +53 -0
- package/dist/presets/lint/plugin/index.d.ts +2 -0
- package/dist/presets/lint/plugin/index.js +6 -2
- package/dist/presets/lint/plugin/options.d.ts +3 -4
- package/dist/presets/lint/plugin/rules/no-query-data-in-state.d.ts +5 -0
- package/dist/presets/lint/plugin/rules/no-query-data-in-state.js +85 -0
- package/dist/presets/lint/plugin/rules/no-react-state-hooks.d.ts +2 -2
- package/dist/presets/lint/plugin/rules/no-react-state-hooks.js +7 -39
- package/dist/presets/lint/plugin/rules/query-from-loader.d.ts +5 -0
- package/dist/presets/lint/plugin/rules/query-from-loader.js +110 -0
- package/dist/presets/lint/tanstack.d.ts +5 -0
- package/dist/presets/lint/tanstack.js +14 -0
- package/dist/presets/rules/index.d.ts +9 -0
- package/dist/presets/rules/index.js +29 -0
- package/package.json +25 -20
- package/skills/adamantite/SKILL.md +70 -4
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { findImportedCalls } from "../imports.js";
|
|
2
|
+
//#region presets/lint/plugin/rules/query-from-loader.ts
|
|
3
|
+
const QUERY_MODULE = "@tanstack/react-query";
|
|
4
|
+
const ROUTER_MODULE = "@tanstack/react-router";
|
|
5
|
+
const QUERY_HOOKS = /* @__PURE__ */ new Set([
|
|
6
|
+
"useInfiniteQuery",
|
|
7
|
+
"useQuery",
|
|
8
|
+
"useSuspenseInfiniteQuery",
|
|
9
|
+
"useSuspenseQuery"
|
|
10
|
+
]);
|
|
11
|
+
const INFINITE_QUERY_HOOKS = /* @__PURE__ */ new Set(["useInfiniteQuery", "useSuspenseInfiniteQuery"]);
|
|
12
|
+
const OPTIONS_FACTORIES = /* @__PURE__ */ new Set(["infiniteQueryOptions", "queryOptions"]);
|
|
13
|
+
/**
|
|
14
|
+
* A file that calls one of these defines a route, in file-based or code-based routing. Only route
|
|
15
|
+
* files have a loader that can preload a query, so the rule checks only them.
|
|
16
|
+
*/
|
|
17
|
+
const ROUTE_FACTORIES = /* @__PURE__ */ new Set([
|
|
18
|
+
"createFileRoute",
|
|
19
|
+
"createLazyFileRoute",
|
|
20
|
+
"createLazyRoute",
|
|
21
|
+
"createRootRoute",
|
|
22
|
+
"createRootRouteWithContext",
|
|
23
|
+
"createRoute"
|
|
24
|
+
]);
|
|
25
|
+
function findVariable(context, identifier) {
|
|
26
|
+
let scope = context.sourceCode.getScope(identifier);
|
|
27
|
+
while (scope !== null && !scope.set.has(identifier.name)) scope = scope.upper;
|
|
28
|
+
return scope?.set.get(identifier.name);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The initializer of a `const` that declares `variable` with a plain identifier, such as `const
|
|
32
|
+
* options = { ... }`. Destructured, `let`, and imported bindings have none.
|
|
33
|
+
*/
|
|
34
|
+
function getConstInit(variable) {
|
|
35
|
+
const [definition] = variable.defs;
|
|
36
|
+
if (variable.defs.length !== 1 || definition?.type !== "Variable") return;
|
|
37
|
+
const { node, parent } = definition;
|
|
38
|
+
if (node.type !== "VariableDeclarator" || node.id.type !== "Identifier" || parent?.type !== "VariableDeclaration" || parent.kind !== "const") return;
|
|
39
|
+
return node.init ?? void 0;
|
|
40
|
+
}
|
|
41
|
+
function isModuleScope(variable) {
|
|
42
|
+
return variable.scope.type === "module" || variable.scope.type === "global";
|
|
43
|
+
}
|
|
44
|
+
const rule = {
|
|
45
|
+
create(context) {
|
|
46
|
+
const hookCalls = [];
|
|
47
|
+
const factoryCalls = /* @__PURE__ */ new Set();
|
|
48
|
+
let isRouteFile = false;
|
|
49
|
+
/**
|
|
50
|
+
* Why `options` does not come from a shared definition, or `undefined` when it does or when the
|
|
51
|
+
* rule cannot tell. `inModule` is true when the expression sits at module scope.
|
|
52
|
+
*/
|
|
53
|
+
function classify(options, inModule) {
|
|
54
|
+
if (options.type === "TSAsExpression" || options.type === "TSNonNullExpression" || options.type === "TSSatisfiesExpression" || options.type === "TSTypeAssertion") return classify(options.expression, inModule);
|
|
55
|
+
if (options.type === "ObjectExpression") return options.properties.some((property) => property.type === "SpreadElement") ? void 0 : "plainObject";
|
|
56
|
+
if (options.type === "CallExpression" && factoryCalls.has(options)) return inModule ? void 0 : "localOptions";
|
|
57
|
+
if (options.type === "Identifier") {
|
|
58
|
+
const variable = findVariable(context, options);
|
|
59
|
+
const init = variable === void 0 ? void 0 : getConstInit(variable);
|
|
60
|
+
return variable === void 0 || init === void 0 ? void 0 : classify(init, isModuleScope(variable));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
ImportDeclaration(node) {
|
|
65
|
+
if (node.source.value === ROUTER_MODULE) {
|
|
66
|
+
isRouteFile ||= findImportedCalls(context, node, ROUTE_FACTORIES).length > 0;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (node.source.value !== QUERY_MODULE) return;
|
|
70
|
+
hookCalls.push(...findImportedCalls(context, node, QUERY_HOOKS));
|
|
71
|
+
for (const { call } of findImportedCalls(context, node, OPTIONS_FACTORIES)) factoryCalls.add(call);
|
|
72
|
+
},
|
|
73
|
+
"Program:exit"() {
|
|
74
|
+
if (!isRouteFile) return;
|
|
75
|
+
for (const { call, name } of hookCalls) {
|
|
76
|
+
const [options] = call.arguments;
|
|
77
|
+
if (options === void 0) continue;
|
|
78
|
+
const messageId = classify(options, false);
|
|
79
|
+
if (messageId !== void 0) {
|
|
80
|
+
const data = INFINITE_QUERY_HOOKS.has(name) ? {
|
|
81
|
+
factory: "infiniteQueryOptions",
|
|
82
|
+
hook: name,
|
|
83
|
+
preload: "ensureInfiniteQueryData"
|
|
84
|
+
} : {
|
|
85
|
+
factory: "queryOptions",
|
|
86
|
+
hook: name,
|
|
87
|
+
preload: "ensureQueryData"
|
|
88
|
+
};
|
|
89
|
+
context.report({
|
|
90
|
+
data,
|
|
91
|
+
messageId,
|
|
92
|
+
node: options
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
meta: {
|
|
100
|
+
docs: { description: "Require TanStack Query hooks in route files to take options defined once with `queryOptions()` or `infiniteQueryOptions()`, so the route loader can preload the same query." },
|
|
101
|
+
messages: {
|
|
102
|
+
localOptions: "`{{hook}}` takes query options created inside a function, so the route loader cannot preload the same query. Move the `{{factory}}()` call to module scope, or into an exported factory such as `userQuery(id)`, preload it in this route's loader with `queryClient.{{preload}}()`, and pass the same options here.",
|
|
103
|
+
plainObject: "`{{hook}}` takes a plain options object instead of options defined with `{{factory}}()`. Define the options once with `{{factory}}()` at module scope or in a shared module, preload them in this route's loader with `queryClient.{{preload}}()`, and pass the same options here."
|
|
104
|
+
},
|
|
105
|
+
schema: [],
|
|
106
|
+
type: "suggestion"
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
//#endregion
|
|
110
|
+
export { rule as default };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region presets/lint/tanstack.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: {
|
|
9
|
+
"adamantite/no-query-data-in-state": "error",
|
|
10
|
+
"adamantite/query-from-loader": "error"
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { config as default };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Context, Diagnostic, ESTree, Fix, Fixer, Node, Options, Plugin, Rule, RuleMeta, SourceCode, Visitor, VisitorWithHooks, defineRule } from "@oxlint/plugins";
|
|
2
|
+
//#region presets/rules/index.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Import every rule file in a rules folder and return them as one Oxlint plugin. The entry module
|
|
5
|
+
* that `custom()` writes calls this function.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadRules(dir: string, name?: string): Promise<Plugin>;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { type Context, type Diagnostic, type ESTree, type Fix, type Fixer, type Node, type Options, type Rule, type RuleMeta, type SourceCode, type Visitor, type VisitorWithHooks, defineRule };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { DEFAULT_PLUGIN_NAME, getRuleName, listRuleFiles } from "../lint/custom.js";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { defineRule } from "@oxlint/plugins";
|
|
5
|
+
//#region presets/rules/index.ts
|
|
6
|
+
async function importRule(path) {
|
|
7
|
+
let module;
|
|
8
|
+
try {
|
|
9
|
+
module = await import(pathToFileURL(path).href);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
12
|
+
throw new Error(`Custom rule ${path} failed to load: ${reason}\nRule files run with type stripping, so use erasable TypeScript only: no enum, namespace, or parameter properties.`, { cause: error });
|
|
13
|
+
}
|
|
14
|
+
if (module.default === void 0) throw new Error(`Custom rule ${path} has no default export. Export the rule as default.`);
|
|
15
|
+
return module.default;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Import every rule file in a rules folder and return them as one Oxlint plugin. The entry module
|
|
19
|
+
* that `custom()` writes calls this function.
|
|
20
|
+
*/
|
|
21
|
+
async function loadRules(dir, name = DEFAULT_PLUGIN_NAME) {
|
|
22
|
+
const rules = await Promise.all(listRuleFiles(dir).map(async (file) => [getRuleName(file), await importRule(join(dir, file))]));
|
|
23
|
+
return {
|
|
24
|
+
meta: { name },
|
|
25
|
+
rules: Object.fromEntries(rules)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { defineRule, loadRules };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adamantite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "An strict and opinionated set of presets for modern TypeScript applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adamantite",
|
|
@@ -54,40 +54,45 @@
|
|
|
54
54
|
"types": "./dist/presets/lint/*.d.ts",
|
|
55
55
|
"default": "./dist/presets/lint/*.js"
|
|
56
56
|
},
|
|
57
|
+
"./rules": {
|
|
58
|
+
"types": "./dist/presets/rules/index.d.ts",
|
|
59
|
+
"default": "./dist/presets/rules/index.js"
|
|
60
|
+
},
|
|
57
61
|
"./typescript": "./dist/presets/tsconfig.json"
|
|
58
62
|
},
|
|
59
63
|
"dependencies": {
|
|
60
|
-
"@clack/prompts": "1.
|
|
64
|
+
"@clack/prompts": "1.8.1",
|
|
61
65
|
"@effect/platform-node": "4.0.0",
|
|
66
|
+
"@oxlint/plugins": "1.86.0",
|
|
62
67
|
"defu": "6.1.7",
|
|
63
68
|
"effect": "4.0.0",
|
|
64
69
|
"jsonc-parser": "3.3.1",
|
|
65
|
-
"nypm": "0.6.
|
|
66
|
-
"oxc-parser": "0.
|
|
70
|
+
"nypm": "0.6.10",
|
|
71
|
+
"oxc-parser": "0.152.0"
|
|
67
72
|
},
|
|
68
73
|
"devDependencies": {
|
|
69
|
-
"@changesets/changelog-github": "1.0.
|
|
70
|
-
"@changesets/cli": "3.0.
|
|
74
|
+
"@changesets/changelog-github": "1.0.1",
|
|
75
|
+
"@changesets/cli": "3.0.3",
|
|
76
|
+
"@effect/tsgo": "0.48.0",
|
|
71
77
|
"@effect/vitest": "4.0.0",
|
|
72
|
-
"@
|
|
73
|
-
"@
|
|
74
|
-
"@tanstack/intent": "0.3.7",
|
|
78
|
+
"@shadcn/lint": "0.2.0",
|
|
79
|
+
"@tanstack/intent": "0.5.0",
|
|
75
80
|
"@types/figlet": "1.7.0",
|
|
76
|
-
"@types/node": "22.20.
|
|
77
|
-
"@vitest/coverage-v8": "5.0.
|
|
78
|
-
"@vitest/ui": "5.0.
|
|
79
|
-
"figlet": "1.
|
|
80
|
-
"knip": "6.
|
|
81
|
-
"oxfmt": "0.
|
|
82
|
-
"oxlint": "1.
|
|
81
|
+
"@types/node": "22.20.5",
|
|
82
|
+
"@vitest/coverage-v8": "5.0.3",
|
|
83
|
+
"@vitest/ui": "5.0.3",
|
|
84
|
+
"figlet": "1.12.0",
|
|
85
|
+
"knip": "6.39.0",
|
|
86
|
+
"oxfmt": "0.71.0",
|
|
87
|
+
"oxlint": "1.86.0",
|
|
83
88
|
"oxlint-plugin-react-doctor": "0.9.14",
|
|
84
|
-
"oxlint-tsgolint": "7.0.
|
|
89
|
+
"oxlint-tsgolint": "7.0.2003",
|
|
85
90
|
"sherif": "1.13.0",
|
|
86
|
-
"tsdown": "0.
|
|
87
|
-
"type-fest": "5.
|
|
91
|
+
"tsdown": "0.23.0",
|
|
92
|
+
"type-fest": "5.10.0",
|
|
88
93
|
"typescript": "7.0.2",
|
|
89
94
|
"unplugin-macros": "0.23.2",
|
|
90
|
-
"vitest": "5.0.
|
|
95
|
+
"vitest": "5.0.3"
|
|
91
96
|
},
|
|
92
97
|
"peerDependencies": {
|
|
93
98
|
"typescript": ">=7"
|
|
@@ -8,6 +8,7 @@ metadata:
|
|
|
8
8
|
sources:
|
|
9
9
|
- "adelrodriguez/adamantite:src/cli.ts"
|
|
10
10
|
- "adelrodriguez/adamantite:src/commands/*.ts"
|
|
11
|
+
- "adelrodriguez/adamantite:src/lib/workspace/custom-rules.ts"
|
|
11
12
|
---
|
|
12
13
|
|
|
13
14
|
# Adamantite
|
|
@@ -28,8 +29,8 @@ npx adamantite init --non-interactive --script check --script fix --typescript -
|
|
|
28
29
|
Repeat `--script`, `--preset`, and `--editor` for multiple values. Available values are:
|
|
29
30
|
|
|
30
31
|
- Scripts: `check`, `fix`, `analyze`. In a detected monorepo, `analyze` also installs Sherif.
|
|
31
|
-
- Presets: `react`, `react-strict`, `react-doctor`, `
|
|
32
|
-
`strict`, `antislop`, `shadcn`;
|
|
32
|
+
- Presets: `react`, `react-strict`, `react-doctor`, `tanstack`, `nextjs`, `vue`, `effect`, `jest`,
|
|
33
|
+
`vitest`, `node`, `strict`, `antislop`, `shadcn`;
|
|
33
34
|
editors: `vscode`, `zed`
|
|
34
35
|
- Optional flags: `--typescript`, `--install-extensions`, `--github-actions`, `--agents`,
|
|
35
36
|
`--overwrite-scripts`
|
|
@@ -46,6 +47,17 @@ properties; keep the object and read its members, or split the pattern. Do not r
|
|
|
46
47
|
`maxDepth` or `maxProperties` only to silence a report. The preset also bans type assertions
|
|
47
48
|
outside tests.
|
|
48
49
|
|
|
50
|
+
The `tanstack` preset needs no extra package. Its `adamantite/query-from-loader` rule reports
|
|
51
|
+
TanStack Query hooks in route files (files that call `createFileRoute`, `createRoute`, or another
|
|
52
|
+
route factory from `@tanstack/react-router`) that take a plain options object or a `queryOptions()` call made inside a
|
|
53
|
+
function; define the query once with `queryOptions()` at module scope or in an exported factory,
|
|
54
|
+
preload it in the route loader with `ensureQueryData()`, and pass the same options to the hook. For
|
|
55
|
+
`useInfiniteQuery` and `useSuspenseInfiniteQuery`, use `infiniteQueryOptions()` and
|
|
56
|
+
`ensureInfiniteQueryData()`. Its
|
|
57
|
+
`adamantite/no-query-data-in-state` rule reports `useState` initialized from query data; read the
|
|
58
|
+
value from the query result and keep only the user's edits in state. Do not wrap options in a
|
|
59
|
+
spread object, or move the hook out of the route file, only to silence `query-from-loader`.
|
|
60
|
+
|
|
49
61
|
The `react-doctor` preset requires the `react` preset and the `oxlint-plugin-react-doctor`
|
|
50
62
|
package, which `init` installs at a pinned version and doctor and `update` keep in sync. It
|
|
51
63
|
enables curated React Doctor rules for React state and effect misuse, such as
|
|
@@ -54,6 +66,18 @@ a modified MIT license: using it as machine learning training data or selling it
|
|
|
54
66
|
hosted product needs written permission from the copyright holder. Tell the user before you
|
|
55
67
|
select the preset.
|
|
56
68
|
|
|
69
|
+
The `effect` preset needs the `@effect/tsgo` package, which `init` installs at a pinned
|
|
70
|
+
version and doctor and `update` keep in sync. Its rules exist only in Oxlint binaries that
|
|
71
|
+
`adamantite prepare` patched, so the `prepare` script in `package.json` must run
|
|
72
|
+
`adamantite prepare`; without the patch, Oxlint stops with `Unknown plugin: 'effecttsgo'`.
|
|
73
|
+
Do not remove the patch or the preset to get past that error: run `adamantite prepare`. If a
|
|
74
|
+
production-only install fails at `adamantite prepare`, add `--ignore-scripts` to that
|
|
75
|
+
install instead of changing the script. `tsconfig.json` needs
|
|
76
|
+
`{ "name": "@effect/language-service", "diagnostics": false }` in `compilerOptions.plugins`,
|
|
77
|
+
and `knip.config.ts` needs `ignoreDependencies.effect`. To allow an unstable Effect module
|
|
78
|
+
on purpose, add it to `allowedUnstableApis` in that tsconfig entry. Do not turn off
|
|
79
|
+
`effecttsgo/unstable-api-usage`.
|
|
80
|
+
|
|
57
81
|
The `shadcn` preset needs Tailwind v4 and the `@shadcn/lint` package, which `init` installs
|
|
58
82
|
at a pinned version and doctor and `update` keep in sync. The plugin finds components and
|
|
59
83
|
the theme through `components.json`. Without that file it looks in `components/ui` or
|
|
@@ -133,8 +157,48 @@ adamantite update
|
|
|
133
157
|
adamantite doctor
|
|
134
158
|
```
|
|
135
159
|
|
|
136
|
-
`update` updates Adamantite-managed dependencies
|
|
137
|
-
findings. Follow those findings and review the resulting diff.
|
|
160
|
+
`update` updates Adamantite-managed dependencies and, with the effect preset, patches Oxlint
|
|
161
|
+
and TypeScript again. It then reports any remaining doctor findings. Follow those findings and review the resulting diff.
|
|
162
|
+
|
|
163
|
+
## Custom rules
|
|
164
|
+
|
|
165
|
+
<!-- Keep this section the same as the AGENTS.md text in src/lib/workspace/custom-rules.ts. -->
|
|
166
|
+
|
|
167
|
+
Each file in the rules folder, `.adamantite/rules/` by default, is an Oxlint rule that this
|
|
168
|
+
project owns. `custom()` from `adamantite/lint/custom` loads the folder and enables each
|
|
169
|
+
rule as `error` under the `project` plugin: `no-process-env.ts` becomes
|
|
170
|
+
`project/no-process-env`. Add a file to add a rule. Delete the file to remove the rule.
|
|
171
|
+
`adamantite rule add <name>` writes a correct stub.
|
|
172
|
+
|
|
173
|
+
- A file that starts with `_`, such as `_helpers.ts`, is a helper, not a rule. Subfolders are
|
|
174
|
+
not read.
|
|
175
|
+
- Export the rule as default with `defineRule` from `adamantite/rules`. The same module exports
|
|
176
|
+
the `Context`, `Rule`, `Visitor`, and `ESTree` node types.
|
|
177
|
+
- Use `createOnce`. It runs one time for each lint run, not one time for each file. Reset
|
|
178
|
+
per-file state in `before`, and report collected results in `after`. `context.filename`,
|
|
179
|
+
`context.sourceCode`, and `context.settings` are not available in the body of
|
|
180
|
+
`createOnce`. Read them in `before` or in a visitor.
|
|
181
|
+
- Write erasable TypeScript only. Oxlint loads rule files with Node.js type stripping, so
|
|
182
|
+
`enum`, a `namespace` with values, parameter properties, and `import x = require()` stop
|
|
183
|
+
the lint run.
|
|
184
|
+
- Rules get no type information. Use only the AST and `context.sourceCode`.
|
|
185
|
+
- Report with a `messageId`, and put each message in `meta.messages`. Describe the options in
|
|
186
|
+
`meta.schema`, and read them from `context.options`.
|
|
187
|
+
- A rule that throws prints an error for each file, and the lint run still exits 0. After you
|
|
188
|
+
change a rule, run the lint and read all of its output.
|
|
189
|
+
- To find node names, run `adamantite rule ast <file>` on real code. It prints the same
|
|
190
|
+
ESTree-shaped AST that rules visit.
|
|
191
|
+
- To test a rule, use `RuleTester` from `oxlint/plugins-dev`. Adamantite does not run rule
|
|
192
|
+
tests.
|
|
193
|
+
- Oxlint caches the rules for each run. Restart the editor language server after you change a
|
|
194
|
+
rule.
|
|
195
|
+
- TypeScript does not check `.adamantite/rules/` by default, because its name starts with a dot. Add
|
|
196
|
+
`".adamantite/rules"` to `include` in `tsconfig.json` to check it.
|
|
197
|
+
- In a monorepo, give each rules folder its own plugin name, such as `custom({ name: "web" })`:
|
|
198
|
+
Oxlint rejects two plugins with the same name in one run. A relative `dir` resolves from the
|
|
199
|
+
file that calls `custom()`, so a shared tooling package can own the rules with
|
|
200
|
+
`custom({ dir: "rules", name: "acme" })`. A nested Oxlint config replaces the root config, so
|
|
201
|
+
a package that needs the root rules extends the root config.
|
|
138
202
|
|
|
139
203
|
## Decision guide
|
|
140
204
|
|
|
@@ -143,4 +207,6 @@ findings. Follow those findings and review the resulting diff.
|
|
|
143
207
|
- Existing project upgrading Adamantite: `update`, then the doctor sequence.
|
|
144
208
|
- Code-quality failure: choose `check` or `analyze` based on the failing subsystem; do not
|
|
145
209
|
reinitialize the project.
|
|
210
|
+
- Project-specific lint rule: add a custom rule with `adamantite rule add <name>`; do not
|
|
211
|
+
edit Adamantite presets.
|
|
146
212
|
- Unknown option or behavior: run `adamantite <command> --help` before guessing.
|