@lewishowles/lint-config 0.6.1 → 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 CHANGED
@@ -1,5 +1,13 @@
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
+
3
11
  ## 0.6.1: 2026-09-21
4
12
 
5
13
  ### Fixes
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, 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.
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 { getDocumentationNode, reportFunctionDocumentation } from "../utils/documentation.js";
2
- import { hasImmediateBlockComment, hasImmediateLineComment } from "../utils/source.js";
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 "../utils/documentation.js";
2
- import { hasImmediateLineComment } from "../utils/source.js";
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 "../utils/jsdoc.js";
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 "../utils/source.js";
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 "../utils/wrap.js";
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,4 +1,4 @@
1
- import { isFunctionValue, reportFunctionDocumentation } from "../utils/documentation.js";
1
+ import { isFunctionValue, reportFunctionDocumentation } from "#comments/utils/documentation.js";
2
2
 
3
3
  /**
4
4
  * Return the declaration node that owns the documentation position.
@@ -1,5 +1,5 @@
1
- import { getDocumentationNode, isFunctionValue } from "../utils/documentation.js";
2
- import { hasImmediateLineComment } from "../utils/source.js";
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 { getCommentNeighbours, isDirectiveComment, isLeadingComment } from "../utils/source.js";
2
- import { getObjectArgument, getObjectProperties, isNamedCall } from "../utils/vue-macro.js";
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 "../utils/vue-macro.js";
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.
@@ -421,6 +421,7 @@ function formatMixedTags(lines, width, addPunctuation) {
421
421
  rest: match[2].trim(),
422
422
  type: match[1],
423
423
  };
424
+
424
425
  preserveSection = false;
425
426
 
426
427
  result.push(formatTagHeader(currentEntry));
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.6.1",
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": {