@lewishowles/lint-config 0.4.0 → 0.5.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 +10 -0
- package/README.md +36 -4
- package/comments/rules/block-comments.js +2 -1
- package/comments/rules/class-documentation.js +8 -5
- package/comments/rules/configured-api-calls.js +4 -2
- package/comments/rules/jsdoc-tag-formatting.js +2 -1
- package/comments/rules/max-line-length.js +14 -4
- package/comments/rules/placement.js +6 -3
- package/comments/rules/sentence-punctuation.js +12 -6
- package/comments/rules/variable-declarations.js +30 -3
- package/comments/rules/vue-component-documentation.js +6 -3
- package/comments/rules/vue-emit-documentation.js +2 -1
- package/comments/utils/jsdoc.js +6 -4
- package/comments/utils/source.js +15 -0
- package/comments.json +9 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0: 2026-09-11
|
|
4
|
+
|
|
5
|
+
### Changes
|
|
6
|
+
|
|
7
|
+
- `comments/variable-declarations`: add a `rootOnly` option; `comments.json` enables it for test files so only root-level variables need comments there. If you override `comments.json` rules, concatenate the test-file override (see README).
|
|
8
|
+
|
|
9
|
+
### Fixes
|
|
10
|
+
|
|
11
|
+
- Measure comment width in display columns, with tabs counted as four, so indented comments wrap and validate against the visible 80-column limit.
|
|
12
|
+
|
|
3
13
|
## 0.4.0: 2026-08-28
|
|
4
14
|
|
|
5
15
|
### New rules
|
package/README.md
CHANGED
|
@@ -92,13 +92,45 @@ The `comments/configured-api-calls` rule requires an immediately preceding line
|
|
|
92
92
|
}
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
The `comments/variable-declarations` rule accepts a `rootOnly` option to require comments only before root-level `const`, `let`, and `using` declarations. The comments layer already enables `rootOnly` for test files matched by `**/*.test.*`, `**/*.spec.*`, and `**/test/**`; other files keep the default of `false`. Override it for another scope or to change the behaviour:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"rules": {
|
|
100
|
+
"comments/variable-declarations": ["error", { "rootOnly": true }]
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
95
105
|
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
106
|
|
|
97
107
|
### `vp check` configuration
|
|
98
108
|
|
|
99
109
|
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
110
|
|
|
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.
|
|
111
|
+
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. Concatenate the `overrides` arrays from every imported layer before the local overrides so shared file-specific rules still apply. Verify the setup with `vp lint --print-config <file>` and check that your real values, not defaults, are active.
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
import { defineConfig } from "vite-plus";
|
|
115
|
+
import lintConfigBase from "@lewishowles/lint-config/base.json" with { type: "json" };
|
|
116
|
+
import lintConfigComments from "@lewishowles/lint-config/comments.json" with { type: "json" };
|
|
117
|
+
import oxlintrc from "./.oxlintrc.json" with { type: "json" };
|
|
118
|
+
|
|
119
|
+
const lint = {
|
|
120
|
+
...lintConfigBase,
|
|
121
|
+
env: oxlintrc.env,
|
|
122
|
+
ignorePatterns: oxlintrc.ignorePatterns,
|
|
123
|
+
jsPlugins: [...lintConfigBase.jsPlugins, ...lintConfigComments.jsPlugins],
|
|
124
|
+
overrides: [
|
|
125
|
+
...(lintConfigBase.overrides ?? []),
|
|
126
|
+
...(lintConfigComments.overrides ?? []),
|
|
127
|
+
...(oxlintrc.overrides ?? []),
|
|
128
|
+
],
|
|
129
|
+
rules: { ...lintConfigBase.rules, ...lintConfigComments.rules },
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export default defineConfig({ lint });
|
|
133
|
+
```
|
|
102
134
|
|
|
103
135
|
## Customising
|
|
104
136
|
|
|
@@ -130,7 +162,7 @@ Ignore patterns are project-specific, so they always live in your project config
|
|
|
130
162
|
|
|
131
163
|
### Adding overrides
|
|
132
164
|
|
|
133
|
-
Overrides are additive: shared overrides
|
|
165
|
+
Overrides are additive in the `vite.config.js` lint block: shared overrides still apply, and your local ones are appended.
|
|
134
166
|
|
|
135
167
|
```json
|
|
136
168
|
{
|
|
@@ -167,7 +199,7 @@ The base layer sorts named members within each import statement, but leaves decl
|
|
|
167
199
|
## What stays repo-local
|
|
168
200
|
|
|
169
201
|
- `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
|
|
202
|
+
- `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. The `vite.config.js` lint block appends these local entries after the shared layer overrides.
|
|
171
203
|
- Rule relaxations for specific file patterns (e.g. turning off `vite-plus/prefer-vite-plus-imports` in generated `.d.ts` files)
|
|
172
204
|
- Additional plugins, only for projects that need them
|
|
173
205
|
|
|
@@ -176,6 +208,6 @@ The base layer sorts named members within each import statement, but leaves decl
|
|
|
176
208
|
When a project's `.oxlintrc.json` extends a shared layer:
|
|
177
209
|
|
|
178
210
|
- **Rules** shallow-merge by key: your value wins for any rule defined in both
|
|
179
|
-
- **Overrides** are additive:
|
|
211
|
+
- **Overrides** are additive in the `vite.config.js` lint block: it concatenates shared layer and local `overrides` entries, including any `env` declared inside an override block
|
|
180
212
|
- **Plugins** are additive: both shared and local `plugins`/`jsPlugins` load, deduplicated
|
|
181
213
|
- **`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.
|
|
@@ -40,7 +40,8 @@ export default {
|
|
|
40
40
|
continue;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
// The comment, with its block structure and delimiters
|
|
43
|
+
// The comment, with its block structure and delimiters
|
|
44
|
+
// normalised.
|
|
44
45
|
const formattedComment = formatJSDocBlockStructure(context.sourceCode, comment);
|
|
45
46
|
|
|
46
47
|
if (formattedComment === commentText) {
|
|
@@ -33,7 +33,8 @@ export default {
|
|
|
33
33
|
* The class declaration node.
|
|
34
34
|
*/
|
|
35
35
|
ClassDeclaration(node) {
|
|
36
|
-
// Resolves any export wrapper before checking for
|
|
36
|
+
// Resolves any export wrapper before checking for
|
|
37
|
+
// documentation.
|
|
37
38
|
const documentationNode = getDocumentationNode(node);
|
|
38
39
|
|
|
39
40
|
if (!hasImmediateBlockComment(context.sourceCode, documentationNode)) {
|
|
@@ -44,8 +45,8 @@ export default {
|
|
|
44
45
|
}
|
|
45
46
|
},
|
|
46
47
|
/**
|
|
47
|
-
* Check class methods for required JSDoc blocks and tags.
|
|
48
|
-
* exempt from the @returns requirement.
|
|
48
|
+
* Check class methods for required JSDoc blocks and tags.
|
|
49
|
+
* Constructors are exempt from the @returns requirement.
|
|
49
50
|
*
|
|
50
51
|
* @param {object} node
|
|
51
52
|
* The method-definition node.
|
|
@@ -93,7 +94,8 @@ export default {
|
|
|
93
94
|
return;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
// Static and instance fields share the requirement; only the
|
|
97
|
+
// Static and instance fields share the requirement; only the
|
|
98
|
+
// wording differs.
|
|
97
99
|
const message = node.static
|
|
98
100
|
? "Static fields require an immediately preceding line comment."
|
|
99
101
|
: "Instance fields require an immediately preceding line comment.";
|
|
@@ -116,7 +118,8 @@ export default {
|
|
|
116
118
|
return;
|
|
117
119
|
}
|
|
118
120
|
|
|
119
|
-
// Resolves any export wrapper before checking for
|
|
121
|
+
// Resolves any export wrapper before checking for
|
|
122
|
+
// documentation.
|
|
120
123
|
const documentationNode = getDocumentationNode(node.parent);
|
|
121
124
|
|
|
122
125
|
if (!hasImmediateBlockComment(context.sourceCode, documentationNode)) {
|
|
@@ -117,8 +117,10 @@ export default {
|
|
|
117
117
|
* The call expression node.
|
|
118
118
|
*/
|
|
119
119
|
CallExpression(node) {
|
|
120
|
-
// Read fresh for every call: createOnce's visitor is shared
|
|
121
|
-
//
|
|
120
|
+
// Read fresh for every call: createOnce's visitor is shared
|
|
121
|
+
// across every file
|
|
122
|
+
// in the run, so caching this at closure-creation time would
|
|
123
|
+
// freeze the first
|
|
122
124
|
// file's options.
|
|
123
125
|
const configuredApis = getConfiguredApis(context);
|
|
124
126
|
|
|
@@ -40,7 +40,8 @@ export default {
|
|
|
40
40
|
continue;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
// The comment, with its tag spacing, order, and grouping
|
|
43
|
+
// The comment, with its tag spacing, order, and grouping
|
|
44
|
+
// normalised.
|
|
44
45
|
const formattedComment = formatJSDocTagFormatting(context.sourceCode, comment);
|
|
45
46
|
|
|
46
47
|
if (formattedComment === commentText) {
|
|
@@ -2,6 +2,7 @@ import { formatJSDocWrapping, isJSDoc } from "../utils/jsdoc.js";
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
getCommentText,
|
|
5
|
+
getDisplayWidth,
|
|
5
6
|
getLineIndent,
|
|
6
7
|
getNewline,
|
|
7
8
|
isDirectiveComment,
|
|
@@ -33,7 +34,7 @@ function formatLineComment(sourceCode, comment) {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
// The available width, allowing for the indent and "// " prefix.
|
|
36
|
-
const width = maximumLineLength - indentation
|
|
37
|
+
const width = maximumLineLength - getDisplayWidth(indentation) - 3;
|
|
37
38
|
// The comment's undecorated text.
|
|
38
39
|
const text = comment.value.trim();
|
|
39
40
|
|
|
@@ -74,7 +75,7 @@ function formatBlockComment(sourceCode, comment) {
|
|
|
74
75
|
// The comment body, sentence-formatted.
|
|
75
76
|
const text = formatSentence(commentText.slice(2, -2).trim());
|
|
76
77
|
// The available width, allowing for the indent and " * " prefix.
|
|
77
|
-
const width = maximumLineLength - indentation
|
|
78
|
+
const width = maximumLineLength - getDisplayWidth(indentation) - 3;
|
|
78
79
|
// The comment body, rewrapped to the available width.
|
|
79
80
|
const lines = wrapWords(text, Math.max(1, width));
|
|
80
81
|
|
|
@@ -120,11 +121,20 @@ export default {
|
|
|
120
121
|
// The comment's individual source lines.
|
|
121
122
|
const lines = commentText.split(/\r\n|\n|\r/);
|
|
122
123
|
|
|
123
|
-
|
|
124
|
+
// The whitespace before the comment, absent when code
|
|
125
|
+
// precedes it.
|
|
126
|
+
const indentation = getLineIndent(context.sourceCode, comment.range[0]) ?? "";
|
|
127
|
+
// The comment lines as they appear on screen. The raw text
|
|
128
|
+
// omits the first line's indentation, so it is restored
|
|
129
|
+
// before measuring.
|
|
130
|
+
const displayLines = [`${indentation}${lines[0]}`, ...lines.slice(1)];
|
|
131
|
+
|
|
132
|
+
if (!displayLines.some((line) => getDisplayWidth(line) > maximumLineLength)) {
|
|
124
133
|
continue;
|
|
125
134
|
}
|
|
126
135
|
|
|
127
|
-
// The comment, rewrapped using the formatter matching its
|
|
136
|
+
// The comment, rewrapped using the formatter matching its
|
|
137
|
+
// type.
|
|
128
138
|
const formattedComment =
|
|
129
139
|
comment.type === "Line"
|
|
130
140
|
? formatLineComment(context.sourceCode, comment)
|
|
@@ -240,7 +240,8 @@ export default {
|
|
|
240
240
|
continue;
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
// The reindentation fixes for the comment and its
|
|
243
|
+
// The reindentation fixes for the comment and its
|
|
244
|
+
// continuations.
|
|
244
245
|
const fixes = getCommentIndentationFixes(
|
|
245
246
|
context.sourceCode,
|
|
246
247
|
comment,
|
|
@@ -249,10 +250,12 @@ export default {
|
|
|
249
250
|
expectedIndent,
|
|
250
251
|
);
|
|
251
252
|
|
|
252
|
-
// The next comment token, used to avoid overlapping gap
|
|
253
|
+
// The next comment token, used to avoid overlapping gap
|
|
254
|
+
// fixes.
|
|
253
255
|
const followingComment = comments[index + 1];
|
|
254
256
|
|
|
255
|
-
// The fix that closes the gap between the comment and its
|
|
257
|
+
// The fix that closes the gap between the comment and its
|
|
258
|
+
// code, when needed.
|
|
256
259
|
const gapFix = getCommentGapFix(
|
|
257
260
|
context.sourceCode,
|
|
258
261
|
comment,
|
|
@@ -136,7 +136,8 @@ function formatOrdinaryBlockComment(sourceCode, comment) {
|
|
|
136
136
|
if (lines[0] === "/*" && lines.at(-1).trim() === "*/" && proseLineIndexes.length > 0) {
|
|
137
137
|
// The comment lines, formatted in place.
|
|
138
138
|
const formattedLines = [...lines];
|
|
139
|
-
// The first and last prose line indexes, which start and end the
|
|
139
|
+
// The first and last prose line indexes, which start and end the
|
|
140
|
+
// sentence.
|
|
140
141
|
const firstProseLine = proseLineIndexes[0];
|
|
141
142
|
// The last prose line index, which ends the sentence.
|
|
142
143
|
const lastProseLine = proseLineIndexes.at(-1);
|
|
@@ -193,13 +194,15 @@ export default {
|
|
|
193
194
|
*/
|
|
194
195
|
Program() {
|
|
195
196
|
for (const commentGroup of getLineCommentGroups(context.sourceCode)) {
|
|
196
|
-
// The group's replacement text, or null when it already
|
|
197
|
+
// The group's replacement text, or null when it already
|
|
198
|
+
// reads as a sentence.
|
|
197
199
|
const formattedGroup = formatLineCommentGroup(context.sourceCode, commentGroup);
|
|
198
200
|
|
|
199
201
|
if (formattedGroup) {
|
|
200
202
|
context.report({
|
|
201
203
|
/**
|
|
202
|
-
* Apply the formatted replacement to the comment
|
|
204
|
+
* Apply the formatted replacement to the comment
|
|
205
|
+
* group.
|
|
203
206
|
*
|
|
204
207
|
* @param {object} fixer
|
|
205
208
|
* The Oxlint fixer.
|
|
@@ -208,7 +211,8 @@ export default {
|
|
|
208
211
|
* The fixes to apply.
|
|
209
212
|
*/
|
|
210
213
|
fix: (fixer) => {
|
|
211
|
-
// The fixes to apply, starting with the first
|
|
214
|
+
// The fixes to apply, starting with the first
|
|
215
|
+
// comment's replacement.
|
|
212
216
|
const fixes = [
|
|
213
217
|
replaceMinimalComment(
|
|
214
218
|
fixer,
|
|
@@ -245,7 +249,8 @@ export default {
|
|
|
245
249
|
// The comment's raw source text.
|
|
246
250
|
const commentText = getCommentText(context.sourceCode, comment);
|
|
247
251
|
|
|
248
|
-
// The comment, sentence-formatted using the JSDoc or
|
|
252
|
+
// The comment, sentence-formatted using the JSDoc or
|
|
253
|
+
// ordinary-block formatter.
|
|
249
254
|
const formattedComment = isJSDoc(commentText)
|
|
250
255
|
? formatJSDocPunctuation(context.sourceCode, comment)
|
|
251
256
|
: formatOrdinaryBlockComment(context.sourceCode, comment);
|
|
@@ -253,7 +258,8 @@ export default {
|
|
|
253
258
|
if (formattedComment !== commentText) {
|
|
254
259
|
context.report({
|
|
255
260
|
/**
|
|
256
|
-
* Apply the sentence-formatted replacement to the
|
|
261
|
+
* Apply the sentence-formatted replacement to the
|
|
262
|
+
* comment.
|
|
257
263
|
*
|
|
258
264
|
* @param {object} fixer
|
|
259
265
|
* The Oxlint fixer.
|
|
@@ -33,6 +33,18 @@ export default {
|
|
|
33
33
|
meta: {
|
|
34
34
|
docs: { description: "Require comments before variable declarations." },
|
|
35
35
|
type: "suggestion",
|
|
36
|
+
schema: [
|
|
37
|
+
{
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: {
|
|
40
|
+
rootOnly: {
|
|
41
|
+
type: "boolean",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
defaultOptions: [{ rootOnly: false }],
|
|
36
48
|
},
|
|
37
49
|
/**
|
|
38
50
|
* Create the rule's node visitors.
|
|
@@ -56,7 +68,8 @@ export default {
|
|
|
56
68
|
return;
|
|
57
69
|
}
|
|
58
70
|
|
|
59
|
-
// A lone const class expression is documented by
|
|
71
|
+
// A lone const class expression is documented by
|
|
72
|
+
// class-documentation.
|
|
60
73
|
if (
|
|
61
74
|
node.kind === "const" &&
|
|
62
75
|
node.declarations.length === 1 &&
|
|
@@ -66,10 +79,24 @@ export default {
|
|
|
66
79
|
return;
|
|
67
80
|
}
|
|
68
81
|
|
|
69
|
-
//
|
|
82
|
+
// The rule's resolved options for the file currently being
|
|
83
|
+
// visited.
|
|
84
|
+
const options = context.options?.[0];
|
|
85
|
+
|
|
86
|
+
// Resolves any export wrapper before checking for
|
|
87
|
+
// documentation.
|
|
70
88
|
const documentationNode = getDocumentationNode(node);
|
|
71
89
|
|
|
72
|
-
|
|
90
|
+
// With rootOnly, only declarations directly under the
|
|
91
|
+
// Program need a comment, so a nested variable inside
|
|
92
|
+
// a function is left alone.
|
|
93
|
+
const shouldCheckDocumentation =
|
|
94
|
+
!options?.rootOnly || documentationNode.parent?.type === "Program";
|
|
95
|
+
|
|
96
|
+
if (
|
|
97
|
+
shouldCheckDocumentation &&
|
|
98
|
+
!hasImmediateLineComment(context.sourceCode, documentationNode)
|
|
99
|
+
) {
|
|
73
100
|
context.report({
|
|
74
101
|
message: "Variable declarations require an immediately preceding line comment.",
|
|
75
102
|
node: documentationNode,
|
|
@@ -119,7 +119,8 @@ function hasComponentDocumentation(context, scriptBlock) {
|
|
|
119
119
|
return false;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
// A blank line would separate the component documentation from its entry
|
|
122
|
+
// A blank line would separate the component documentation from its entry
|
|
123
|
+
// point.
|
|
123
124
|
const commentGap = context.sourceCode.text.slice(0, comment.range[0]);
|
|
124
125
|
|
|
125
126
|
return immediateCommentGapPattern.test(commentGap);
|
|
@@ -142,7 +143,8 @@ export default {
|
|
|
142
143
|
createOnce(context) {
|
|
143
144
|
return {
|
|
144
145
|
/**
|
|
145
|
-
* Check the current component's script setup block for
|
|
146
|
+
* Check the current component's script setup block for
|
|
147
|
+
* documentation.
|
|
146
148
|
*
|
|
147
149
|
* @param {object} node
|
|
148
150
|
* The entry point parsed from the current script block.
|
|
@@ -150,7 +152,8 @@ export default {
|
|
|
150
152
|
Program(node) {
|
|
151
153
|
// createOnce builds this visitor once for the whole run, and a
|
|
152
154
|
// single file's script blocks are not necessarily visited
|
|
153
|
-
// consecutively, so the matching block is looked up fresh on
|
|
155
|
+
// consecutively, so the matching block is looked up fresh on
|
|
156
|
+
// each
|
|
154
157
|
// call rather than tracked with shared state.
|
|
155
158
|
const scriptBlock = findScriptBlock(context);
|
|
156
159
|
|
|
@@ -111,7 +111,8 @@ export default {
|
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
// Array and type-only forms have no runtime properties to
|
|
114
|
+
// Array and type-only forms have no runtime properties to
|
|
115
|
+
// document.
|
|
115
116
|
const emitsObject = getObjectArgument(node, 0);
|
|
116
117
|
|
|
117
118
|
if (emitsObject) {
|
package/comments/utils/jsdoc.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getCommentText, getLineIndent, getNewline } from "./source.js";
|
|
1
|
+
import { getCommentText, getDisplayWidth, getLineIndent, getNewline } from "./source.js";
|
|
2
2
|
import { addTerminalPunctuation, capitaliseSentence, formatSentence, wrapWords } from "./wrap.js";
|
|
3
3
|
|
|
4
4
|
// The JSDoc tags this package formats, in their required output order.
|
|
@@ -217,7 +217,8 @@ function formatUnwrappedProse(lines, addPunctuation) {
|
|
|
217
217
|
// The formatted lines, built up in place.
|
|
218
218
|
const result = lines.map((line) => line.trim());
|
|
219
219
|
|
|
220
|
-
// The index of the current paragraph's first line, or null between
|
|
220
|
+
// The index of the current paragraph's first line, or null between
|
|
221
|
+
// paragraphs.
|
|
221
222
|
let paragraphStart = null;
|
|
222
223
|
|
|
223
224
|
for (let index = 0; index < result.length; index += 1) {
|
|
@@ -336,7 +337,8 @@ function formatTags(tagLines, width, addPunctuation, normaliseTags) {
|
|
|
336
337
|
// The formatted lines, built up in place.
|
|
337
338
|
const result = [];
|
|
338
339
|
|
|
339
|
-
// The tag type of the previously written entry, used to detect group
|
|
340
|
+
// The tag type of the previously written entry, used to detect group
|
|
341
|
+
// changes.
|
|
340
342
|
let lastType = null;
|
|
341
343
|
|
|
342
344
|
// The entries regrouped into the required tag order.
|
|
@@ -526,7 +528,7 @@ function getJSDocFormattingContext(sourceCode, comment) {
|
|
|
526
528
|
// The newline style used by the surrounding source.
|
|
527
529
|
const newline = getNewline(sourceCode.text);
|
|
528
530
|
// The available content width, allowing for the indent and " * " prefix.
|
|
529
|
-
const width = Math.max(1, 80 - indent
|
|
531
|
+
const width = Math.max(1, 80 - getDisplayWidth(indent) - 3);
|
|
530
532
|
// The undecorated comment content lines.
|
|
531
533
|
const content = getJSDocContent(commentText);
|
|
532
534
|
// The content split into its prose and tag sections.
|
package/comments/utils/source.js
CHANGED
|
@@ -156,6 +156,21 @@ export function getLineIndent(sourceCode, offset) {
|
|
|
156
156
|
return /^\s*$/.test(prefix) ? prefix : null;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Return the number of columns a source string occupies on screen, so comment
|
|
161
|
+
* lines are measured and wrapped against the 80-column limit under tab
|
|
162
|
+
* indentation. A tab counts as four columns, the width this package assumes.
|
|
163
|
+
*
|
|
164
|
+
* @param {string} sourceText
|
|
165
|
+
* The source string to measure, such as a line or its indentation.
|
|
166
|
+
*
|
|
167
|
+
* @returns {number}
|
|
168
|
+
* The source string's width in display columns.
|
|
169
|
+
*/
|
|
170
|
+
export function getDisplayWidth(sourceText) {
|
|
171
|
+
return sourceText.replaceAll("\t", " ").length;
|
|
172
|
+
}
|
|
173
|
+
|
|
159
174
|
/**
|
|
160
175
|
* Return the source items immediately around a comment.
|
|
161
176
|
*
|
package/comments.json
CHANGED
|
@@ -19,5 +19,13 @@
|
|
|
19
19
|
"comments/vue-component-documentation": "error",
|
|
20
20
|
"comments/vue-emit-documentation": "error",
|
|
21
21
|
"comments/vue-prop-documentation": "error"
|
|
22
|
-
}
|
|
22
|
+
},
|
|
23
|
+
"overrides": [
|
|
24
|
+
{
|
|
25
|
+
"files": ["**/*.test.*", "**/*.spec.*", "**/test/**"],
|
|
26
|
+
"rules": {
|
|
27
|
+
"comments/variable-declarations": ["error", { "rootOnly": true }]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
]
|
|
23
31
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lewishowles/lint-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Shared oxlint configuration for Lewis Howles projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"config",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"lint": "vp check",
|
|
41
41
|
"lint:fix": "vp check --fix",
|
|
42
42
|
"prepare": "vp config --no-agent",
|
|
43
|
-
"publint": "publint"
|
|
43
|
+
"publint": "publint",
|
|
44
|
+
"test:unit": "node --test test/comments/*.test.js"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"publint": "^0.3.22",
|