@lewishowles/lint-config 0.6.0 → 0.7.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 +14 -0
- package/README.md +24 -6
- package/base.json +20 -2
- package/comments/rules/class-documentation.js +5 -2
- package/comments/rules/configured-api-calls.js +2 -2
- package/comments/rules/formatting.js +4 -5
- package/comments/rules/function-documentation.js +1 -1
- package/comments/rules/variable-declarations.js +2 -2
- package/comments/rules/vue-component-documentation.js +1 -1
- package/comments/rules/vue-emit-documentation.js +6 -2
- package/comments/rules/vue-prop-documentation.js +6 -2
- package/comments/utils/documentation.js +32 -12
- package/comments/utils/jsdoc.js +1 -0
- package/imports.json +13 -0
- package/package.json +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0: 2026-09-30
|
|
4
|
+
|
|
5
|
+
### Changes
|
|
6
|
+
|
|
7
|
+
- Breaking: `base` now reports an error for imports that reach into a parent folder, such as `../utils`. Use the package's subpath imports or an alias instead; `--fix` can't rewrite these.
|
|
8
|
+
- Breaking: `base` now expects a blank line before an `await` statement that follows other code, and around `const` declarations that span several lines. Run `--fix` once to update existing code.
|
|
9
|
+
- New opt-in `imports.json` layer with formatter settings that sort imports. See the README for how to add it.
|
|
10
|
+
|
|
11
|
+
## 0.6.1: 2026-09-21
|
|
12
|
+
|
|
13
|
+
### Fixes
|
|
14
|
+
|
|
15
|
+
- `comments/function-documentation`: accept the name the JSDoc gives a destructured object parameter, instead of always expecting `options`.
|
|
16
|
+
|
|
3
17
|
## 0.6.0: 2026-09-21
|
|
4
18
|
|
|
5
19
|
### Changes
|
package/README.md
CHANGED
|
@@ -186,15 +186,33 @@ Plugins are additive and deduplicated: your local plugins are added to the share
|
|
|
186
186
|
|
|
187
187
|
## Layers
|
|
188
188
|
|
|
189
|
-
| Layer | File | Contents
|
|
190
|
-
| ---------- | --------------- |
|
|
191
|
-
| `base` | `base.json` | Correctness and formatting rules, import sorting, `oxc`/`typescript`/`unicorn` plugins |
|
|
192
|
-
| `comments` | `comments.json` | Optional comment-formatting rules, variable-declaration documentation, JSDoc checks
|
|
193
|
-
| `vue` | `vue.json` | Extends `base`, adds the `vue` plugin, Vue compiler macro globals, Vue-specific rules
|
|
189
|
+
| Layer | File | Contents |
|
|
190
|
+
| ---------- | --------------- | ----------------------------------------------------------------------------------------------- |
|
|
191
|
+
| `base` | `base.json` | Correctness and formatting rules, import sorting, `import`/`oxc`/`typescript`/`unicorn` plugins |
|
|
192
|
+
| `comments` | `comments.json` | Optional comment-formatting rules, variable-declaration documentation, JSDoc checks |
|
|
193
|
+
| `vue` | `vue.json` | Extends `base`, adds the `vue` plugin, Vue compiler macro globals, Vue-specific rules |
|
|
194
194
|
|
|
195
195
|
### Import sorting
|
|
196
196
|
|
|
197
|
-
The base layer sorts named members within each import statement
|
|
197
|
+
The base lint layer sorts named members within each import statement. To sort whole import statements, opt in to `imports.json`. It contains Oxfmt settings, not an Oxlint layer, so add it to the `fmt` block in `vite.config.js`:
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
import { defineConfig } from "vite-plus";
|
|
201
|
+
import importFormat from "@lewishowles/lint-config/imports.json" with { type: "json" };
|
|
202
|
+
import oxfmtrc from "./.oxfmtrc.json" with { type: "json" };
|
|
203
|
+
|
|
204
|
+
export default defineConfig({
|
|
205
|
+
fmt: { ...oxfmtrc, ...importFormat },
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
If your `vite.config.js` already has a `lint` block, add `fmt` to the same `defineConfig` call: `defineConfig({ lint, fmt: { ...oxfmtrc, ...importFormat } })`.
|
|
210
|
+
|
|
211
|
+
This puts named imports first, including `import type { … }` and imports with both default and named members. Other default imports come second, along with `import type` default imports and namespace imports (`import * as`). `.vue` imports come last. Oxfmt sorts each group by module path, ignoring letter case, and separates the groups with blank lines. Side-effect imports keep their written order and position, so keep them at the top or bottom of your imports: one written among the other imports stays where it is and splits the group around it.
|
|
212
|
+
|
|
213
|
+
### Parent-folder imports
|
|
214
|
+
|
|
215
|
+
The base layer reports imports from a parent folder (`../`), with no automatic fix. Same-folder (`./`), `@/` alias and package `#` subpath imports are allowed. After upgrading, any existing `../` imports fail lint until they move to an `@/` alias or, in a package without one, to [package subpath imports](https://nodejs.org/api/packages.html#subpath-imports).
|
|
198
216
|
|
|
199
217
|
## What stays repo-local
|
|
200
218
|
|
package/base.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"plugins": ["oxc", "typescript", "unicorn"],
|
|
2
|
+
"plugins": ["import", "oxc", "typescript", "unicorn"],
|
|
3
3
|
"jsPlugins": [
|
|
4
4
|
"@stylistic/eslint-plugin",
|
|
5
5
|
{
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"no-unexpected-multiline": "error",
|
|
26
26
|
"no-useless-assignment": "error",
|
|
27
27
|
"preserve-caught-error": "error",
|
|
28
|
+
"import/no-relative-parent-imports": "error",
|
|
28
29
|
"sort-imports": [
|
|
29
30
|
"error",
|
|
30
31
|
{
|
|
@@ -62,7 +63,24 @@
|
|
|
62
63
|
{ "blankLine": "never", "prev": "const", "next": "const" },
|
|
63
64
|
{ "blankLine": "never", "prev": "let", "next": "let" },
|
|
64
65
|
{ "blankLine": "always", "prev": "multiline-const", "next": "*" },
|
|
65
|
-
{ "blankLine": "always", "prev": "*", "next": "multiline-const" }
|
|
66
|
+
{ "blankLine": "always", "prev": "*", "next": "multiline-const" },
|
|
67
|
+
{
|
|
68
|
+
"blankLine": "always",
|
|
69
|
+
"prev": "*",
|
|
70
|
+
"next": { "selector": "ExpressionStatement[expression.type=\"AwaitExpression\"]" }
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"blankLine": "always",
|
|
74
|
+
"prev": { "selector": "ExpressionStatement[expression.type=\"AwaitExpression\"]" },
|
|
75
|
+
"next": "*"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"blankLine": "never",
|
|
79
|
+
"prev": { "selector": "ExpressionStatement[expression.type=\"AwaitExpression\"]" },
|
|
80
|
+
"next": { "selector": "ExpressionStatement[expression.type=\"AwaitExpression\"]" }
|
|
81
|
+
},
|
|
82
|
+
{ "blankLine": "always", "prev": "multiline-expression", "next": "*" },
|
|
83
|
+
{ "blankLine": "always", "prev": "*", "next": "multiline-expression" }
|
|
66
84
|
],
|
|
67
85
|
|
|
68
86
|
"vite-plus/prefer-vite-plus-imports": "error"
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
getDocumentationNode,
|
|
3
|
+
reportFunctionDocumentation,
|
|
4
|
+
} from "#comments/utils/documentation.js";
|
|
5
|
+
import { hasImmediateBlockComment, hasImmediateLineComment } from "#comments/utils/source.js";
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Create the class-documentation rule.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getDocumentationNode } from "
|
|
2
|
-
import { hasImmediateLineComment } from "
|
|
1
|
+
import { getDocumentationNode } from "#comments/utils/documentation.js";
|
|
2
|
+
import { hasImmediateLineComment } from "#comments/utils/source.js";
|
|
3
3
|
|
|
4
4
|
// The built-in APIs that require a preceding comment by default.
|
|
5
5
|
const builtInApis = new Set([
|
|
@@ -5,8 +5,7 @@ import {
|
|
|
5
5
|
formatJSDocWrapping,
|
|
6
6
|
hasTargetJSDocTag,
|
|
7
7
|
isJSDoc,
|
|
8
|
-
} from "
|
|
9
|
-
|
|
8
|
+
} from "#comments/utils/jsdoc.js";
|
|
10
9
|
import {
|
|
11
10
|
getCommentNeighbours,
|
|
12
11
|
getCommentText,
|
|
@@ -18,15 +17,14 @@ import {
|
|
|
18
17
|
isDirectiveComment,
|
|
19
18
|
isLeadingComment,
|
|
20
19
|
replaceMinimalComment,
|
|
21
|
-
} from "
|
|
22
|
-
|
|
20
|
+
} from "#comments/utils/source.js";
|
|
23
21
|
import {
|
|
24
22
|
addTerminalPunctuation,
|
|
25
23
|
capitaliseSentence,
|
|
26
24
|
formatSentence,
|
|
27
25
|
refillCommentLines,
|
|
28
26
|
wrapWords,
|
|
29
|
-
} from "
|
|
27
|
+
} from "#comments/utils/wrap.js";
|
|
30
28
|
|
|
31
29
|
// The line length this rule wraps comments to.
|
|
32
30
|
const maximumLineLength = 80;
|
|
@@ -207,6 +205,7 @@ function formatOrdinaryBlockComment(sourceCode, comment) {
|
|
|
207
205
|
formattedLines[firstProseLine],
|
|
208
206
|
capitaliseSentence,
|
|
209
207
|
);
|
|
208
|
+
|
|
210
209
|
formattedLines[lastProseLine] = formatBlockCommentLine(
|
|
211
210
|
formattedLines[lastProseLine],
|
|
212
211
|
addTerminalPunctuation,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getDocumentationNode, isFunctionValue } from "
|
|
2
|
-
import { hasImmediateLineComment } from "
|
|
1
|
+
import { getDocumentationNode, isFunctionValue } from "#comments/utils/documentation.js";
|
|
2
|
+
import { hasImmediateLineComment } from "#comments/utils/source.js";
|
|
3
3
|
|
|
4
4
|
// Declaration kinds that require an immediately preceding line comment.
|
|
5
5
|
const documentedKinds = new Set(["await using", "const", "let", "using"]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { isDirectiveComment } from "#comments/utils/source.js";
|
|
1
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { isDirectiveComment } from "../utils/source.js";
|
|
3
3
|
|
|
4
4
|
// Captures the opening tag's attributes separately, so the setup attribute can
|
|
5
5
|
// be tested without the tag being available from Oxlint's extracted AST.
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
getCommentNeighbours,
|
|
3
|
+
isDirectiveComment,
|
|
4
|
+
isLeadingComment,
|
|
5
|
+
} from "#comments/utils/source.js";
|
|
6
|
+
import { getObjectArgument, getObjectProperties, isNamedCall } from "#comments/utils/vue-macro.js";
|
|
3
7
|
|
|
4
8
|
// Matches the single newline and indentation allowed between a comment and an
|
|
5
9
|
// event.
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCommentNeighbours,
|
|
3
|
+
isDirectiveComment,
|
|
4
|
+
isLeadingComment,
|
|
5
|
+
} from "#comments/utils/source.js";
|
|
1
6
|
import {
|
|
2
7
|
getObjectArgument,
|
|
3
8
|
getObjectProperties,
|
|
4
9
|
getPropertyName,
|
|
5
10
|
isNamedCall,
|
|
6
|
-
} from "
|
|
7
|
-
import { getCommentNeighbours, isDirectiveComment, isLeadingComment } from "../utils/source.js";
|
|
11
|
+
} from "#comments/utils/vue-macro.js";
|
|
8
12
|
|
|
9
13
|
// Matches the single newline and indentation allowed between a comment and a
|
|
10
14
|
// prop.
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { getJSDocContent, isJSDoc } from "./jsdoc.js";
|
|
2
|
-
import { getPropertyName } from "./vue-macro.js";
|
|
3
|
-
|
|
4
2
|
import {
|
|
5
3
|
getCommentNeighbours,
|
|
6
4
|
getCommentText,
|
|
7
5
|
isDirectiveComment,
|
|
8
6
|
isLeadingComment,
|
|
9
7
|
} from "./source.js";
|
|
8
|
+
import { getPropertyName } from "./vue-macro.js";
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Return the declaration node that owns the documentation position.
|
|
@@ -157,11 +156,14 @@ function getObjectPatternPaths(sourceCode, node, parentPath) {
|
|
|
157
156
|
* The Oxlint source code object.
|
|
158
157
|
* @param {object} node
|
|
159
158
|
* The parameter node.
|
|
159
|
+
* @param {string} [rootPath]
|
|
160
|
+
* The documented name that starts each path for a destructured object
|
|
161
|
+
* parameter. Defaults to options.
|
|
160
162
|
*
|
|
161
163
|
* @returns {string[]}
|
|
162
164
|
* The required JSDoc parameter paths.
|
|
163
165
|
*/
|
|
164
|
-
function getParameterPaths(sourceCode, node) {
|
|
166
|
+
function getParameterPaths(sourceCode, node, rootPath = "options") {
|
|
165
167
|
if (node.type === "Identifier") {
|
|
166
168
|
return [node.name];
|
|
167
169
|
}
|
|
@@ -171,11 +173,11 @@ function getParameterPaths(sourceCode, node) {
|
|
|
171
173
|
}
|
|
172
174
|
|
|
173
175
|
if (node.type === "ObjectPattern") {
|
|
174
|
-
return getObjectPatternPaths(sourceCode, node,
|
|
176
|
+
return getObjectPatternPaths(sourceCode, node, rootPath);
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
if (node.type === "AssignmentPattern") {
|
|
178
|
-
return getParameterPaths(sourceCode, node.left);
|
|
180
|
+
return getParameterPaths(sourceCode, node.left, rootPath);
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
return [];
|
|
@@ -189,25 +191,40 @@ function getParameterPaths(sourceCode, node) {
|
|
|
189
191
|
* @param {object} comment
|
|
190
192
|
* The JSDoc comment token.
|
|
191
193
|
*
|
|
192
|
-
* @returns {
|
|
193
|
-
*
|
|
194
|
+
* @returns {object}
|
|
195
|
+
* An object with names, the set of every documented parameter path, and
|
|
196
|
+
* topLevelNames, the top-level names in the order they are documented.
|
|
194
197
|
*/
|
|
195
198
|
function getDocumentedParameters(sourceCode, comment) {
|
|
196
199
|
// Splits the JSDoc block into its individual lines.
|
|
197
200
|
const content = getJSDocContent(getCommentText(sourceCode, comment));
|
|
198
201
|
// Collects the parameter paths documented by @param tags.
|
|
199
202
|
const names = new Set();
|
|
203
|
+
// Collects top-level parameter names in their documented order.
|
|
204
|
+
const topLevelNames = [];
|
|
200
205
|
|
|
201
206
|
for (const line of content) {
|
|
202
207
|
// Matches an @param tag and captures its documented path.
|
|
203
208
|
const match = line.trim().match(/^@param(?:\s+\{[^}]+\})?\s+(\[[^\]]+\]|\S+)/);
|
|
204
209
|
|
|
205
210
|
if (match) {
|
|
206
|
-
|
|
211
|
+
// The documented name as written, including optional brackets and
|
|
212
|
+
// any default value.
|
|
213
|
+
const name = match[1];
|
|
214
|
+
|
|
215
|
+
names.add(name);
|
|
216
|
+
|
|
217
|
+
// Removes optional and default-value syntax before checking for a
|
|
218
|
+
// nested path.
|
|
219
|
+
const topLevelName = name.replace(/^\[|\]$/g, "").split("=")[0];
|
|
220
|
+
|
|
221
|
+
if (!topLevelName.includes(".")) {
|
|
222
|
+
topLevelNames.push(topLevelName);
|
|
223
|
+
}
|
|
207
224
|
}
|
|
208
225
|
}
|
|
209
226
|
|
|
210
|
-
return names;
|
|
227
|
+
return { names, topLevelNames };
|
|
211
228
|
}
|
|
212
229
|
|
|
213
230
|
/**
|
|
@@ -306,11 +323,14 @@ export function reportFunctionDocumentation(context, node, functionNode, options
|
|
|
306
323
|
}
|
|
307
324
|
|
|
308
325
|
// Reads the parameter paths already documented by @param tags.
|
|
309
|
-
const documentedParameters = getDocumentedParameters(
|
|
326
|
+
const { names: documentedParameters, topLevelNames } = getDocumentedParameters(
|
|
327
|
+
context.sourceCode,
|
|
328
|
+
comment,
|
|
329
|
+
);
|
|
310
330
|
|
|
311
331
|
// Derives the parameter paths the function actually requires.
|
|
312
|
-
const parameterPaths = functionNode.params.flatMap((parameter) =>
|
|
313
|
-
getParameterPaths(context.sourceCode, parameter),
|
|
332
|
+
const parameterPaths = functionNode.params.flatMap((parameter, index) =>
|
|
333
|
+
getParameterPaths(context.sourceCode, parameter, topLevelNames[index]),
|
|
314
334
|
);
|
|
315
335
|
|
|
316
336
|
for (const path of parameterPaths) {
|
package/comments/utils/jsdoc.js
CHANGED
package/imports.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sortImports": {
|
|
3
|
+
"groups": ["named", "default", "vue"],
|
|
4
|
+
"customGroups": [
|
|
5
|
+
{ "groupName": "vue", "elementNamePattern": ["**/*.vue"] },
|
|
6
|
+
{ "groupName": "named", "modifiers": ["named"] },
|
|
7
|
+
{ "groupName": "default", "modifiers": ["default"] }
|
|
8
|
+
],
|
|
9
|
+
"newlinesBetween": true,
|
|
10
|
+
"order": "asc",
|
|
11
|
+
"sortSideEffects": false
|
|
12
|
+
}
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lewishowles/lint-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Shared oxlint configuration for Lewis Howles projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"config",
|
|
@@ -23,14 +23,19 @@
|
|
|
23
23
|
"base.json",
|
|
24
24
|
"comments",
|
|
25
25
|
"comments.json",
|
|
26
|
+
"imports.json",
|
|
26
27
|
"vue.json",
|
|
27
28
|
"README.md"
|
|
28
29
|
],
|
|
29
30
|
"type": "module",
|
|
31
|
+
"imports": {
|
|
32
|
+
"#comments/*": "./comments/*"
|
|
33
|
+
},
|
|
30
34
|
"exports": {
|
|
31
35
|
"./base.json": "./base.json",
|
|
32
36
|
"./comments.json": "./comments.json",
|
|
33
37
|
"./comments/plugin": "./comments/plugin.js",
|
|
38
|
+
"./imports.json": "./imports.json",
|
|
34
39
|
"./vue.json": "./vue.json"
|
|
35
40
|
},
|
|
36
41
|
"publishConfig": {
|