@lewishowles/lint-config 0.2.0 → 0.4.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/CHANGELOG.md +15 -0
- package/README.md +87 -64
- package/base.json +57 -57
- package/comments/plugin.js +32 -0
- package/comments/rules/block-comments.js +70 -0
- package/comments/rules/class-documentation.js +131 -0
- package/comments/rules/configured-api-calls.js +143 -0
- package/comments/rules/function-documentation.js +111 -0
- package/comments/rules/jsdoc-tag-formatting.js +70 -0
- package/comments/rules/line-comments.js +86 -0
- package/comments/rules/max-line-length.js +159 -0
- package/comments/rules/placement.js +290 -0
- package/comments/rules/sentence-punctuation.js +275 -0
- package/comments/rules/variable-declarations.js +88 -0
- package/comments/rules/vue-component-documentation.js +169 -0
- package/comments/rules/vue-emit-documentation.js +123 -0
- package/comments/rules/vue-prop-documentation.js +224 -0
- package/comments/utils/documentation.js +349 -0
- package/comments/utils/jsdoc.js +756 -0
- package/comments/utils/source.js +346 -0
- package/comments/utils/vue-macro.js +70 -0
- package/comments/utils/wrap.js +118 -0
- package/comments.json +23 -0
- package/package.json +12 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.4.0: 2026-08-28
|
|
4
|
+
|
|
5
|
+
### New rules
|
|
6
|
+
|
|
7
|
+
- Added `comments/class-documentation`: requires a block comment on class declarations and const-assigned class expressions, JSDoc on constructors and ordinary methods, return-aware JSDoc on getters and setters, and line comments on instance and static fields. Ships in the opt-in `comments.json` layer.
|
|
8
|
+
|
|
9
|
+
### Fixes
|
|
10
|
+
|
|
11
|
+
- A directive comment (`eslint-`, `oxlint-`, and similar) sitting between a doc comment and its code is now reported, instead of the doc being treated as attached to the code.
|
|
12
|
+
- Documentation placed before an exported declaration is now recognised.
|
|
13
|
+
- `using` and `await using` declarations now require a preceding line comment, like `const` and `let`.
|
|
14
|
+
|
|
15
|
+
Earlier versions predate this changelog; see the git history for their changes.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @lewishowles/lint-config
|
|
2
2
|
|
|
3
|
-
Shared
|
|
3
|
+
Shared Oxlint configuration for Lewis Howles projects. Projects extend this package to keep their lint configuration consistent across projects, instead of copying and maintaining the same rules everywhere.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -19,28 +19,94 @@ Create a `.oxlintrc.json` in your project root that extends the appropriate laye
|
|
|
19
19
|
```json
|
|
20
20
|
{
|
|
21
21
|
"extends": ["./node_modules/@lewishowles/lint-config/base.json"],
|
|
22
|
+
"env": { "builtin": true, "browser": true },
|
|
22
23
|
"ignorePatterns": ["**/dist/*", ".codebase-memory/**"]
|
|
23
24
|
}
|
|
24
25
|
```
|
|
25
26
|
|
|
27
|
+
Note that `env` has to be redeclared here: Oxlint doesn't yet merge it through `extends`, so `base.json`'s own `env` never reaches your project. See [known limitations](docs/limitations.md) for why.
|
|
28
|
+
|
|
26
29
|
### Vue layer (Vue 3 projects)
|
|
27
30
|
|
|
28
31
|
```json
|
|
29
32
|
{
|
|
30
33
|
"extends": ["./node_modules/@lewishowles/lint-config/vue.json"],
|
|
34
|
+
"env": { "builtin": true, "browser": true },
|
|
35
|
+
"globals": {
|
|
36
|
+
"defineEmits": "readonly",
|
|
37
|
+
"defineExpose": "readonly",
|
|
38
|
+
"defineModel": "readonly",
|
|
39
|
+
"defineOptions": "readonly",
|
|
40
|
+
"defineProps": "readonly",
|
|
41
|
+
"defineSlots": "readonly",
|
|
42
|
+
"withDefaults": "readonly"
|
|
43
|
+
},
|
|
44
|
+
"ignorePatterns": ["**/dist/*", ".codebase-memory/**"]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The Vue layer extends `base.json` internally, so you only need to extend `vue.json`. The same `env`/`globals` limitation applies here too, which is why both are redeclared above.
|
|
49
|
+
|
|
50
|
+
### Comment formatting (optional)
|
|
51
|
+
|
|
52
|
+
Add the comments layer alongside the base or Vue layer to enforce the comment-formatting rules, variable-declaration documentation, JSDoc on named functions and first-level object methods, documentation directly after each Vue `<script setup>` opening tag, and block comments for runtime `defineProps` properties:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"extends": [
|
|
57
|
+
"./node_modules/@lewishowles/lint-config/base.json",
|
|
58
|
+
"./node_modules/@lewishowles/lint-config/comments.json"
|
|
59
|
+
],
|
|
60
|
+
"env": { "builtin": true, "browser": true },
|
|
31
61
|
"ignorePatterns": ["**/dist/*", ".codebase-memory/**"]
|
|
32
62
|
}
|
|
33
63
|
```
|
|
34
64
|
|
|
35
|
-
The Vue
|
|
65
|
+
The Vue component rule reads the raw `.vue` file because Oxlint's JS Plugin API only receives the extracted script block. The comments layer loads its plugin for you, so there's no relative `jsPlugins` path to add. To pick rules yourself instead, add the plugin directly:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"jsPlugins": [
|
|
70
|
+
{
|
|
71
|
+
"name": "comments",
|
|
72
|
+
"specifier": "@lewishowles/lint-config/comments/plugin"
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"rules": {
|
|
76
|
+
"comments/line-comments": "error"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The `comments/vue-prop-documentation` rule requires an indented block comment immediately before every runtime property in `defineProps`. A matching comment on a `defineProps` property also documents its `withDefaults` entry; type-only props are not checked.
|
|
82
|
+
|
|
83
|
+
The `comments/vue-emit-documentation` rule requires an indented block comment immediately before every runtime property in `defineEmits`. Function-valued events also require the normal JSDoc tags; array-form and type-only emits are not checked.
|
|
84
|
+
|
|
85
|
+
The `comments/configured-api-calls` rule requires an immediately preceding line comment before configured bare-identifier calls such as Vue lifecycle hooks, reactive effects, and `onClickOutside`. A documented variable declaration covers a direct call initializer; member-expression calls are out of scope. Add project-specific APIs without replacing the built-in list:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"rules": {
|
|
90
|
+
"comments/configured-api-calls": ["error", { "additionalApis": ["subscribe"] }]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The `comments/class-documentation` rule requires an immediately preceding block comment before class declarations and const-assigned class expressions. Constructors, methods, getters, and setters require full JSDoc; constructors never need an `@returns` tag, and getters and setters need one only when they return a value. Instance and static fields require an immediately preceding line comment.
|
|
96
|
+
|
|
97
|
+
### `vp check` configuration
|
|
98
|
+
|
|
99
|
+
The `vp check` and `vp lint` commands read Oxlint settings only from a `lint` block in `vite.config.js`; they do not read `.oxlintrc.json` directly. If `vite.config.js` is missing, they silently use an unrelated default configuration. You may still see plausible warnings and exit codes, but none of your rules are applied.
|
|
100
|
+
|
|
101
|
+
Each consuming repo needs a `vite.config.js` with a `lint` block built from the config layer(s) and the repo's `.oxlintrc.json`, imported as JSON. Verify the setup with `vp lint --print-config <file>` and check that your real values, not defaults, are active.
|
|
36
102
|
|
|
37
103
|
## Customising
|
|
38
104
|
|
|
39
|
-
Your `.oxlintrc.json`
|
|
105
|
+
Your project's `.oxlintrc.json` can override rules, add ignore patterns, add overrides, or add plugins on top of the shared layer.
|
|
40
106
|
|
|
41
107
|
### Overriding a rule
|
|
42
108
|
|
|
43
|
-
To change the severity or options of a rule defined in the shared layer, redeclare it in your
|
|
109
|
+
To change the severity or options of a rule defined in the shared layer, redeclare it in your project config: your value wins.
|
|
44
110
|
|
|
45
111
|
```json
|
|
46
112
|
{
|
|
@@ -53,7 +119,7 @@ To change the severity or options of a rule defined in the shared layer, redecla
|
|
|
53
119
|
|
|
54
120
|
### Adding ignore patterns
|
|
55
121
|
|
|
56
|
-
Ignore patterns are
|
|
122
|
+
Ignore patterns are project-specific, so they always live in your project config:
|
|
57
123
|
|
|
58
124
|
```json
|
|
59
125
|
{
|
|
@@ -84,75 +150,32 @@ Overrides are additive: shared overrides (if any) still apply, and your local on
|
|
|
84
150
|
|
|
85
151
|
### Adding plugins
|
|
86
152
|
|
|
87
|
-
Plugins are additive and deduplicated: your local plugins are added to the shared ones.
|
|
153
|
+
Plugins are additive and deduplicated: your local plugins are added to the shared ones. Oxlint's `plugins` field only accepts built-in plugin names, such as `oxc`, `typescript`, `unicorn`, and `vue`; there's no `playwright` or `vitest` plugin. Custom JS plugins, like this package's `comments` plugin, load through `jsPlugins` instead. Test-file-specific behaviour is handled via `overrides`, not plugins.
|
|
88
154
|
|
|
89
155
|
## Layers
|
|
90
156
|
|
|
91
|
-
| Layer
|
|
92
|
-
|
|
|
93
|
-
| `base`
|
|
94
|
-
| `
|
|
95
|
-
|
|
96
|
-
### Import sorting ownership
|
|
157
|
+
| Layer | File | Contents |
|
|
158
|
+
| ---------- | --------------- | -------------------------------------------------------------------------------------- |
|
|
159
|
+
| `base` | `base.json` | Correctness and formatting rules, import sorting, `oxc`/`typescript`/`unicorn` plugins |
|
|
160
|
+
| `comments` | `comments.json` | Optional comment-formatting rules, variable-declaration documentation, JSDoc checks |
|
|
161
|
+
| `vue` | `vue.json` | Extends `base`, adds the `vue` plugin, Vue compiler macro globals, Vue-specific rules |
|
|
97
162
|
|
|
98
|
-
|
|
163
|
+
### Import sorting
|
|
99
164
|
|
|
100
|
-
|
|
165
|
+
The base layer sorts named members within each import statement, but leaves declaration order (which import comes first) to Oxfmt: enable Oxfmt's `sortImports` option in your local `.oxfmtrc.json` if you want that sorted and fixed automatically.
|
|
101
166
|
|
|
102
167
|
## What stays repo-local
|
|
103
168
|
|
|
104
|
-
- `ignorePatterns`, since every
|
|
105
|
-
- `overrides` for
|
|
106
|
-
-
|
|
107
|
-
- Additional plugins, only for
|
|
169
|
+
- `ignorePatterns`, since every project has different build output and tool directories
|
|
170
|
+
- `overrides` for project-specific directories (e.g. `bin/**/*.js`, `src/cli/**/*.js`, `src/playwright/**/*.js`), since the file paths differ per project and can't be generalised
|
|
171
|
+
- Rule relaxations for specific file patterns (e.g. turning off `vite-plus/prefer-vite-plus-imports` in generated `.d.ts` files)
|
|
172
|
+
- Additional plugins, only for projects that need them
|
|
108
173
|
|
|
109
174
|
## Merge semantics
|
|
110
175
|
|
|
111
|
-
When a
|
|
176
|
+
When a project's `.oxlintrc.json` extends a shared layer:
|
|
112
177
|
|
|
113
|
-
- **Rules** shallow-merge by key:
|
|
178
|
+
- **Rules** shallow-merge by key: your value wins for any rule defined in both
|
|
114
179
|
- **Overrides** are additive: both shared and local `overrides` entries apply, including any `env` declared inside an override block
|
|
115
|
-
- **Plugins** are additive: both shared and local `plugins`/`jsPlugins`
|
|
116
|
-
|
|
117
|
-
### Known oxlint limitation: top-level `env`, `globals`, and `ignorePatterns` don't merge through `extends`
|
|
118
|
-
|
|
119
|
-
oxlint currently drops top-level `env`, `globals`, and `ignorePatterns` from an extended config file entirely: they only take effect if declared directly in the file oxlint is invoked with. This is an open upstream bug: [oxc-project/oxc#20087](https://github.com/oxc-project/oxc/issues/20087) (open as of oxlint 1.72.0).
|
|
120
|
-
|
|
121
|
-
In practice this means:
|
|
122
|
-
|
|
123
|
-
- `base.json`'s `env` (`builtin`, `browser`) and `vue.json`'s Vue macro `globals` (`defineProps`, `defineEmits`, etc.) will **not** reach a consumer that only does `{ "extends": ["./node_modules/@lewishowles/lint-config/vue.json"] }`: every global from the shared layer will be flagged by `no-undef`.
|
|
124
|
-
- Any `ignorePatterns` this package might declare would be silently dropped the same way, so it deliberately ships none. See "What stays repo-local" below.
|
|
125
|
-
|
|
126
|
-
Until this is fixed upstream, redeclare the `env`/`globals` you need directly in your project's `.oxlintrc.json`, even though `base.json`/`vue.json` already declare them:
|
|
127
|
-
|
|
128
|
-
```json
|
|
129
|
-
{
|
|
130
|
-
"extends": ["./node_modules/@lewishowles/lint-config/vue.json"],
|
|
131
|
-
"env": { "builtin": true, "browser": true },
|
|
132
|
-
"globals": {
|
|
133
|
-
"defineEmits": "readonly",
|
|
134
|
-
"defineExpose": "readonly",
|
|
135
|
-
"defineModel": "readonly",
|
|
136
|
-
"defineOptions": "readonly",
|
|
137
|
-
"defineProps": "readonly",
|
|
138
|
-
"defineSlots": "readonly",
|
|
139
|
-
"withDefaults": "readonly"
|
|
140
|
-
},
|
|
141
|
-
"ignorePatterns": ["**/dist/*", ".codebase-memory/**"]
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Known limitation: `vite-plus`'s `lint` config field requires resolved objects, not string paths
|
|
146
|
-
|
|
147
|
-
Raw oxlint (CLI, editor integrations) accepts `"extends": ["./node_modules/@lewishowles/lint-config/vue.json"]` as string paths and resolves them at load time. `vite-plus`, when a project routes its oxlint config through `vite.config.js`'s `lint` field (importing `.oxlintrc.json` as JSON and handing it to `vp check`/`vp lint`), does not resolve string paths in `extends`: every entry, at every nesting level, must already be a plain object. This means `vue.json`'s own internal `extends: ["./base.json"]` also breaks one level deeper.
|
|
148
|
-
|
|
149
|
-
If your project uses `vite-plus`'s `lint` field rather than raw oxlint, resolve the chain yourself in `vite.config.js`:
|
|
150
|
-
|
|
151
|
-
```js
|
|
152
|
-
import base from "@lewishowles/lint-config/base.json" with { type: "json" };
|
|
153
|
-
import vue from "@lewishowles/lint-config/vue.json" with { type: "json" };
|
|
154
|
-
|
|
155
|
-
const lint = { ...vue, extends: [base, ...(vue.extends ?? [])] };
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
`.oxlintrc.json` itself should stay untouched (string `extends`) for raw oxlint/editor consumption; this only applies to the `vite-plus` config path.
|
|
180
|
+
- **Plugins** are additive: both shared and local `plugins`/`jsPlugins` load, deduplicated
|
|
181
|
+
- **`env`, `globals`, and `ignorePatterns` don't merge through `extends` at all** (an open Oxlint bug), which is why the usage examples above redeclare `env`/`globals` directly. See [known limitations](docs/limitations.md) for the full detail, including the separate `vite-plus` caveat around resolving `extends` paths.
|
package/base.json
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
2
|
+
"plugins": ["oxc", "typescript", "unicorn"],
|
|
3
|
+
"jsPlugins": [
|
|
4
|
+
"@stylistic/eslint-plugin",
|
|
5
|
+
{
|
|
6
|
+
"name": "vite-plus",
|
|
7
|
+
"specifier": "vite-plus/oxlint-plugin"
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"categories": {
|
|
11
|
+
"correctness": "error"
|
|
12
|
+
},
|
|
13
|
+
"env": {
|
|
14
|
+
"builtin": true,
|
|
15
|
+
"browser": true
|
|
16
|
+
},
|
|
17
|
+
"rules": {
|
|
18
|
+
"no-case-declarations": "error",
|
|
19
|
+
"no-empty": "error",
|
|
20
|
+
"no-fallthrough": "error",
|
|
21
|
+
"no-prototype-builtins": "error",
|
|
22
|
+
"no-redeclare": "error",
|
|
23
|
+
"no-regex-spaces": "error",
|
|
24
|
+
"no-undef": "error",
|
|
25
|
+
"no-unexpected-multiline": "error",
|
|
26
|
+
"no-useless-assignment": "error",
|
|
27
|
+
"preserve-caught-error": "error",
|
|
28
|
+
"sort-imports": [
|
|
29
|
+
"error",
|
|
30
|
+
{
|
|
31
|
+
"ignoreDeclarationSort": true
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
"@stylistic/no-confusing-arrow": "error",
|
|
36
|
+
"@stylistic/padding-line-between-statements": [
|
|
37
|
+
"error",
|
|
38
|
+
{ "blankLine": "always", "prev": "const", "next": "let" },
|
|
39
|
+
{ "blankLine": "always", "prev": "let", "next": "const" },
|
|
40
|
+
{ "blankLine": "always", "prev": "*", "next": "break" },
|
|
41
|
+
{ "blankLine": "always", "prev": ["const", "let"], "next": "*" },
|
|
42
|
+
{ "blankLine": "always", "prev": "*", "next": "return" },
|
|
43
|
+
{ "blankLine": "any", "prev": "const", "next": "const" },
|
|
44
|
+
{ "blankLine": "any", "prev": "let", "next": "let" },
|
|
45
|
+
{ "blankLine": "always", "prev": "multiline-const", "next": "*" },
|
|
46
|
+
{ "blankLine": "always", "prev": "*", "next": "multiline-const" }
|
|
47
|
+
],
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
"vite-plus/prefer-vite-plus-imports": "error"
|
|
50
|
+
},
|
|
51
|
+
"overrides": [
|
|
52
|
+
{
|
|
53
|
+
"files": ["**/vite.config.*", "**/vitest.config.*", "**/playwright*.config.*"],
|
|
54
|
+
"env": { "node": true }
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"options": {
|
|
58
|
+
"typeAware": false,
|
|
59
|
+
"typeCheck": false
|
|
60
|
+
}
|
|
61
61
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import blockComments from "./rules/block-comments.js";
|
|
2
|
+
import classDocumentation from "./rules/class-documentation.js";
|
|
3
|
+
import configuredApiCalls from "./rules/configured-api-calls.js";
|
|
4
|
+
import functionDocumentation from "./rules/function-documentation.js";
|
|
5
|
+
import jsdocTagFormatting from "./rules/jsdoc-tag-formatting.js";
|
|
6
|
+
import lineComments from "./rules/line-comments.js";
|
|
7
|
+
import maxLineLength from "./rules/max-line-length.js";
|
|
8
|
+
import placement from "./rules/placement.js";
|
|
9
|
+
import sentencePunctuation from "./rules/sentence-punctuation.js";
|
|
10
|
+
import variableDeclarations from "./rules/variable-declarations.js";
|
|
11
|
+
import vueComponentDocumentation from "./rules/vue-component-documentation.js";
|
|
12
|
+
import vueEmitDocumentation from "./rules/vue-emit-documentation.js";
|
|
13
|
+
import vuePropDocumentation from "./rules/vue-prop-documentation.js";
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
meta: { name: "comments" },
|
|
17
|
+
rules: {
|
|
18
|
+
"block-comments": blockComments,
|
|
19
|
+
"class-documentation": classDocumentation,
|
|
20
|
+
"configured-api-calls": configuredApiCalls,
|
|
21
|
+
"function-documentation": functionDocumentation,
|
|
22
|
+
"jsdoc-tag-formatting": jsdocTagFormatting,
|
|
23
|
+
"line-comments": lineComments,
|
|
24
|
+
"max-line-length": maxLineLength,
|
|
25
|
+
placement,
|
|
26
|
+
"sentence-punctuation": sentencePunctuation,
|
|
27
|
+
"variable-declarations": variableDeclarations,
|
|
28
|
+
"vue-component-documentation": vueComponentDocumentation,
|
|
29
|
+
"vue-emit-documentation": vueEmitDocumentation,
|
|
30
|
+
"vue-prop-documentation": vuePropDocumentation,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { formatJSDocBlockStructure, isJSDoc } from "../utils/jsdoc.js";
|
|
2
|
+
import { getCommentText, replaceMinimalComment } from "../utils/source.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create the JSDoc block-comment formatting rule.
|
|
6
|
+
*
|
|
7
|
+
* @returns {object}
|
|
8
|
+
* The Oxlint rule definition.
|
|
9
|
+
*/
|
|
10
|
+
export default {
|
|
11
|
+
meta: {
|
|
12
|
+
docs: { description: "Format JSDoc block comments." },
|
|
13
|
+
fixable: "code",
|
|
14
|
+
type: "layout",
|
|
15
|
+
},
|
|
16
|
+
/**
|
|
17
|
+
* Create the rule's node visitors.
|
|
18
|
+
*
|
|
19
|
+
* @param {object} context
|
|
20
|
+
* The Oxlint rule context.
|
|
21
|
+
*
|
|
22
|
+
* @returns {object}
|
|
23
|
+
* The visitor functions for this rule.
|
|
24
|
+
*/
|
|
25
|
+
createOnce(context) {
|
|
26
|
+
return {
|
|
27
|
+
/**
|
|
28
|
+
* Format every JSDoc comment's block structure in the file.
|
|
29
|
+
*/
|
|
30
|
+
Program() {
|
|
31
|
+
for (const comment of context.sourceCode.getAllComments()) {
|
|
32
|
+
if (comment.type !== "Block") {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// The comment's raw source text.
|
|
37
|
+
const commentText = getCommentText(context.sourceCode, comment);
|
|
38
|
+
|
|
39
|
+
if (!isJSDoc(commentText)) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// The comment, with its block structure and delimiters normalised.
|
|
44
|
+
const formattedComment = formatJSDocBlockStructure(context.sourceCode, comment);
|
|
45
|
+
|
|
46
|
+
if (formattedComment === commentText) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
context.report({
|
|
51
|
+
/**
|
|
52
|
+
* Apply the formatted replacement to the comment.
|
|
53
|
+
*
|
|
54
|
+
* @param {object} fixer
|
|
55
|
+
* The Oxlint fixer.
|
|
56
|
+
*
|
|
57
|
+
* @returns {object}
|
|
58
|
+
* The fix to apply.
|
|
59
|
+
*/
|
|
60
|
+
fix: (fixer) => {
|
|
61
|
+
return replaceMinimalComment(fixer, comment, commentText, formattedComment);
|
|
62
|
+
},
|
|
63
|
+
message: "JSDoc comments must use the configured block format.",
|
|
64
|
+
node: comment,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { getDocumentationNode, reportFunctionDocumentation } from "../utils/documentation.js";
|
|
2
|
+
import { hasImmediateBlockComment, hasImmediateLineComment } from "../utils/source.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create the class-documentation rule.
|
|
6
|
+
*
|
|
7
|
+
* @returns {object}
|
|
8
|
+
* The Oxlint rule definition.
|
|
9
|
+
*/
|
|
10
|
+
export default {
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description:
|
|
14
|
+
"Require documentation for classes, their methods, getters, setters, and fields.",
|
|
15
|
+
},
|
|
16
|
+
type: "suggestion",
|
|
17
|
+
},
|
|
18
|
+
/**
|
|
19
|
+
* Create the rule's node visitors.
|
|
20
|
+
*
|
|
21
|
+
* @param {object} context
|
|
22
|
+
* The Oxlint rule context.
|
|
23
|
+
*
|
|
24
|
+
* @returns {object}
|
|
25
|
+
* The visitor functions for this rule.
|
|
26
|
+
*/
|
|
27
|
+
createOnce(context) {
|
|
28
|
+
return {
|
|
29
|
+
/**
|
|
30
|
+
* Check a class declaration for a preceding block comment.
|
|
31
|
+
*
|
|
32
|
+
* @param {object} node
|
|
33
|
+
* The class declaration node.
|
|
34
|
+
*/
|
|
35
|
+
ClassDeclaration(node) {
|
|
36
|
+
// Resolves any export wrapper before checking for documentation.
|
|
37
|
+
const documentationNode = getDocumentationNode(node);
|
|
38
|
+
|
|
39
|
+
if (!hasImmediateBlockComment(context.sourceCode, documentationNode)) {
|
|
40
|
+
context.report({
|
|
41
|
+
message: "Classes require an immediately preceding block comment.",
|
|
42
|
+
node: documentationNode,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
/**
|
|
47
|
+
* Check class methods for required JSDoc blocks and tags. Constructors are
|
|
48
|
+
* exempt from the @returns requirement.
|
|
49
|
+
*
|
|
50
|
+
* @param {object} node
|
|
51
|
+
* The method-definition node.
|
|
52
|
+
*/
|
|
53
|
+
MethodDefinition(node) {
|
|
54
|
+
if (node.kind === "constructor") {
|
|
55
|
+
reportFunctionDocumentation(context, node, node.value, {
|
|
56
|
+
requiresReturns: false,
|
|
57
|
+
subject: "Constructors",
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (node.kind === "get") {
|
|
64
|
+
reportFunctionDocumentation(context, node, node.value, {
|
|
65
|
+
subject: "Getters",
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (node.kind === "set") {
|
|
72
|
+
reportFunctionDocumentation(context, node, node.value, {
|
|
73
|
+
subject: "Setters",
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (node.kind === "method") {
|
|
80
|
+
reportFunctionDocumentation(context, node, node.value, {
|
|
81
|
+
subject: "Methods",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
/**
|
|
86
|
+
* Check instance and static fields for a preceding line comment.
|
|
87
|
+
*
|
|
88
|
+
* @param {object} node
|
|
89
|
+
* The property-definition node.
|
|
90
|
+
*/
|
|
91
|
+
PropertyDefinition(node) {
|
|
92
|
+
if (hasImmediateLineComment(context.sourceCode, node)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Static and instance fields share the requirement; only the wording differs.
|
|
97
|
+
const message = node.static
|
|
98
|
+
? "Static fields require an immediately preceding line comment."
|
|
99
|
+
: "Instance fields require an immediately preceding line comment.";
|
|
100
|
+
|
|
101
|
+
context.report({ message, node });
|
|
102
|
+
},
|
|
103
|
+
/**
|
|
104
|
+
* Check a const class expression for a preceding block comment.
|
|
105
|
+
*
|
|
106
|
+
* @param {object} node
|
|
107
|
+
* The variable declarator node.
|
|
108
|
+
*/
|
|
109
|
+
VariableDeclarator(node) {
|
|
110
|
+
if (
|
|
111
|
+
node.parent?.kind !== "const" ||
|
|
112
|
+
node.parent.declarations.length !== 1 ||
|
|
113
|
+
node.id?.type !== "Identifier" ||
|
|
114
|
+
node.init?.type !== "ClassExpression"
|
|
115
|
+
) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Resolves any export wrapper before checking for documentation.
|
|
120
|
+
const documentationNode = getDocumentationNode(node.parent);
|
|
121
|
+
|
|
122
|
+
if (!hasImmediateBlockComment(context.sourceCode, documentationNode)) {
|
|
123
|
+
context.report({
|
|
124
|
+
message: "Classes require an immediately preceding block comment.",
|
|
125
|
+
node: documentationNode,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
};
|