@wowlab/eslint-config 0.1.2 → 0.2.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 +7 -2
- package/dist/core/base.js +7 -1
- package/dist/core/perfectionist.js +15 -2
- package/dist/core/plugin.d.ts +2 -0
- package/dist/core/plugin.js +6 -0
- package/dist/core/rules/function-padding.d.ts +2 -0
- package/dist/core/rules/function-padding.js +81 -0
- package/dist/core/stylistic.js +1 -1
- package/dist/presets/react.d.ts +2 -1
- package/dist/presets/react.js +16 -2
- package/dist/presets/typescript.js +3 -3
- package/package.json +17 -17
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Composable ESLint flat configs for JavaScript and TypeScript
|
|
|
7
7
|
## Getting started
|
|
8
8
|
|
|
9
9
|
```console
|
|
10
|
-
pnpm add --save-dev @wowlab/eslint-config eslint typescript
|
|
10
|
+
pnpm add --save-dev @wowlab/eslint-config eslint@10 typescript@6.0
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
```js
|
|
@@ -38,12 +38,17 @@ export default createConfig({
|
|
|
38
38
|
|
|
39
39
|
`createConfig` includes the base rules. Consumer rules and overrides are applied last.
|
|
40
40
|
|
|
41
|
+
Requires ESLint 10.4 or newer within version 10 and TypeScript 5.6 through 6.0.
|
|
42
|
+
Runtime dependencies use exact versions so plugin updates are tested before release.
|
|
43
|
+
Transitive dependencies can still update; commit your project's lockfile and generate
|
|
44
|
+
it using the same pnpm release-age policy as CI.
|
|
45
|
+
|
|
41
46
|
## Presets
|
|
42
47
|
|
|
43
48
|
- `deno` adds Deno globals.
|
|
44
49
|
- `next` adds the Next.js Core Web Vitals rules.
|
|
45
50
|
- `node` adds Node.js globals.
|
|
46
|
-
- `react` adds React rules for JavaScript. Set `typescript: true` to add TypeScript support or `typeChecked: true` to add
|
|
51
|
+
- `react` adds React rules for JavaScript. Set `typescript: true` to add TypeScript support or `typeChecked: true` to add type-aware React and TypeScript rules. Set `typescriptTypeChecked: false` when only the React rules need type information.
|
|
47
52
|
- `tanstackQuery` adds TanStack Query rules.
|
|
48
53
|
- `typescript` adds TypeScript rules, optionally using the project service.
|
|
49
54
|
|
package/dist/core/base.js
CHANGED
|
@@ -4,6 +4,7 @@ import sonarjs from "eslint-plugin-sonarjs";
|
|
|
4
4
|
import switchCase from "eslint-plugin-switch-case";
|
|
5
5
|
import unicorn from "eslint-plugin-unicorn";
|
|
6
6
|
import { perfectionistConfig } from "./perfectionist.js";
|
|
7
|
+
import { wowlabPlugin } from "./plugin.js";
|
|
7
8
|
import { stylisticConfig } from "./stylistic.js";
|
|
8
9
|
const ruleOverrides = {
|
|
9
10
|
"@eslint-community/eslint-comments/disable-enable-pair": [
|
|
@@ -37,6 +38,11 @@ export function createBaseConfig({ internalPatterns = [], } = {}) {
|
|
|
37
38
|
name: "wowlab/javascript",
|
|
38
39
|
},
|
|
39
40
|
perfectionistConfig(internalPatterns),
|
|
41
|
+
{
|
|
42
|
+
name: "wowlab/layout",
|
|
43
|
+
plugins: { wowlab: wowlabPlugin },
|
|
44
|
+
rules: { "wowlab/function-padding": "error" },
|
|
45
|
+
},
|
|
40
46
|
stylisticConfig,
|
|
41
47
|
{
|
|
42
48
|
name: "wowlab/switch-case",
|
|
@@ -49,7 +55,7 @@ export function createBaseConfig({ internalPatterns = [], } = {}) {
|
|
|
49
55
|
],
|
|
50
56
|
},
|
|
51
57
|
},
|
|
52
|
-
unicorn.configs
|
|
58
|
+
unicorn.configs.recommended,
|
|
53
59
|
sonarjs.configs.recommended,
|
|
54
60
|
comments.recommended,
|
|
55
61
|
{ name: "wowlab/rule-defaults", rules: ruleOverrides },
|
|
@@ -30,7 +30,14 @@ export function perfectionistConfig(internalPatterns) {
|
|
|
30
30
|
rules: {
|
|
31
31
|
curly: ["error", "all"],
|
|
32
32
|
"perfectionist/sort-array-includes": "warn",
|
|
33
|
-
"perfectionist/sort-classes": [
|
|
33
|
+
"perfectionist/sort-classes": [
|
|
34
|
+
"warn",
|
|
35
|
+
{
|
|
36
|
+
groups: CLASS_GROUPS,
|
|
37
|
+
newlinesBetween: "ignore",
|
|
38
|
+
newlinesInside: "ignore",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
34
41
|
"perfectionist/sort-decorators": "warn",
|
|
35
42
|
"perfectionist/sort-enums": ["warn", { partitionByComment: true }],
|
|
36
43
|
"perfectionist/sort-exports": "warn",
|
|
@@ -39,7 +46,13 @@ export function perfectionistConfig(internalPatterns) {
|
|
|
39
46
|
"perfectionist/sort-interfaces": "warn",
|
|
40
47
|
"perfectionist/sort-intersection-types": "warn",
|
|
41
48
|
"perfectionist/sort-maps": ["warn", { partitionByComment: true }],
|
|
42
|
-
"perfectionist/sort-modules":
|
|
49
|
+
"perfectionist/sort-modules": [
|
|
50
|
+
"warn",
|
|
51
|
+
{
|
|
52
|
+
newlinesBetween: "ignore",
|
|
53
|
+
newlinesInside: "ignore",
|
|
54
|
+
},
|
|
55
|
+
],
|
|
43
56
|
"perfectionist/sort-named-exports": "warn",
|
|
44
57
|
"perfectionist/sort-named-imports": "warn",
|
|
45
58
|
"perfectionist/sort-objects": ["warn", { partitionByComment: true }],
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
function functionDeclaration(statement) {
|
|
2
|
+
const declaration = statement.type === "ExportDefaultDeclaration" ||
|
|
3
|
+
statement.type === "ExportNamedDeclaration"
|
|
4
|
+
? statement.declaration
|
|
5
|
+
: statement;
|
|
6
|
+
if (!declaration ||
|
|
7
|
+
(declaration.type !== "FunctionDeclaration" &&
|
|
8
|
+
declaration.type !== "TSDeclareFunction")) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
implementation: declaration.type === "FunctionDeclaration" && declaration.body != null,
|
|
13
|
+
name: declaration.id?.name,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function isSameOverloadGroup(current, previous) {
|
|
17
|
+
return (!previous.implementation &&
|
|
18
|
+
current.name != null &&
|
|
19
|
+
current.name === previous.name);
|
|
20
|
+
}
|
|
21
|
+
export const functionPadding = {
|
|
22
|
+
create(context) {
|
|
23
|
+
const sourceCode = context.sourceCode;
|
|
24
|
+
function firstAttachedLine(current, previous) {
|
|
25
|
+
let startLine = current.loc?.start.line ?? 0;
|
|
26
|
+
const comments = sourceCode.getCommentsBefore(current);
|
|
27
|
+
for (let index = comments.length - 1; index >= 0; index -= 1) {
|
|
28
|
+
const comment = comments[index];
|
|
29
|
+
if (!comment?.loc ||
|
|
30
|
+
comment.loc.start.line <= (previous.loc?.end.line ?? 0) ||
|
|
31
|
+
startLine > comment.loc.end.line + 1) {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
startLine = comment.loc.start.line;
|
|
35
|
+
}
|
|
36
|
+
return startLine;
|
|
37
|
+
}
|
|
38
|
+
function checkStatements(statements) {
|
|
39
|
+
for (let index = 1; index < statements.length; index += 1) {
|
|
40
|
+
const current = statements[index];
|
|
41
|
+
const previous = statements[index - 1];
|
|
42
|
+
if (!current || !previous || !current.loc || !previous.loc) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const currentFunction = functionDeclaration(current);
|
|
46
|
+
const previousFunction = functionDeclaration(previous);
|
|
47
|
+
const startLine = firstAttachedLine(current, previous);
|
|
48
|
+
if (!currentFunction ||
|
|
49
|
+
!previousFunction ||
|
|
50
|
+
isSameOverloadGroup(currentFunction, previousFunction) ||
|
|
51
|
+
startLine > previous.loc.end.line + 1) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const lineStart = sourceCode.text.lastIndexOf("\n", sourceCode.getIndexFromLoc({ column: 0, line: startLine }) - 1);
|
|
55
|
+
context.report({
|
|
56
|
+
fix: (fixer) => fixer.insertTextBeforeRange([lineStart + 1, lineStart + 1], "\n"),
|
|
57
|
+
messageId: "expected",
|
|
58
|
+
node: current,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
BlockStatement: (node) => checkStatements(node.body),
|
|
64
|
+
Program: (node) => checkStatements(node.body),
|
|
65
|
+
StaticBlock: (node) => checkStatements(node.body),
|
|
66
|
+
SwitchCase: (node) => checkStatements(node.consequent),
|
|
67
|
+
TSModuleBlock: (node) => checkStatements(node.body),
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
meta: {
|
|
71
|
+
docs: {
|
|
72
|
+
description: "Require blank lines around function declaration groups",
|
|
73
|
+
},
|
|
74
|
+
fixable: "whitespace",
|
|
75
|
+
messages: {
|
|
76
|
+
expected: "Expected a blank line around the function declaration.",
|
|
77
|
+
},
|
|
78
|
+
schema: [],
|
|
79
|
+
type: "layout",
|
|
80
|
+
},
|
|
81
|
+
};
|
package/dist/core/stylistic.js
CHANGED
|
@@ -68,7 +68,7 @@ const structuralRules = {
|
|
|
68
68
|
{ blankLine: "always", next: "*", prev: ["if", "for", "while", "switch", "try", "function", "class"] },
|
|
69
69
|
{ blankLine: "always", next: "block-like", prev: "block-like" },
|
|
70
70
|
{ blankLine: "always", next: "export", prev: "*" },
|
|
71
|
-
{ blankLine: "any", next: "function", prev: "function" },
|
|
71
|
+
{ blankLine: "any", next: ["function", "function-overload"], prev: ["function", "function-overload"] },
|
|
72
72
|
{ blankLine: "any", next: "export", prev: "export" },
|
|
73
73
|
],
|
|
74
74
|
"@stylistic/quotes": [
|
package/dist/presets/react.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export interface ReactOptions extends FilesOptions {
|
|
|
5
5
|
typeChecked?: boolean;
|
|
6
6
|
typescript?: boolean;
|
|
7
7
|
typescriptFiles?: string[];
|
|
8
|
+
typescriptTypeChecked?: boolean;
|
|
8
9
|
}
|
|
9
|
-
export declare function react({ files, tsconfigRootDir, typeChecked, typescript, typescriptFiles, }?: ReactOptions): Linter.Config[];
|
|
10
|
+
export declare function react({ files, tsconfigRootDir, typeChecked, typescript, typescriptFiles, typescriptTypeChecked, }?: ReactOptions): Linter.Config[];
|
package/dist/presets/react.js
CHANGED
|
@@ -3,7 +3,7 @@ import reactHooks from "eslint-plugin-react-hooks";
|
|
|
3
3
|
import { typescript as typescriptPreset } from "./typescript.js";
|
|
4
4
|
const JAVASCRIPT_FILES = ["**/*.{js,jsx,mjs,cjs}"];
|
|
5
5
|
const TYPESCRIPT_FILES = ["**/*.{ts,tsx,mts,cts}"];
|
|
6
|
-
export function react({ files = JAVASCRIPT_FILES, tsconfigRootDir, typeChecked = false, typescript = false, typescriptFiles = TYPESCRIPT_FILES, } = {}) {
|
|
6
|
+
export function react({ files = JAVASCRIPT_FILES, tsconfigRootDir, typeChecked = false, typescript = false, typescriptFiles = TYPESCRIPT_FILES, typescriptTypeChecked = typeChecked, } = {}) {
|
|
7
7
|
const typescriptConfigName = typeChecked
|
|
8
8
|
? "recommended-type-checked"
|
|
9
9
|
: "recommended";
|
|
@@ -35,8 +35,22 @@ export function react({ files = JAVASCRIPT_FILES, tsconfigRootDir, typeChecked =
|
|
|
35
35
|
...typescriptPreset({
|
|
36
36
|
files: typescriptFiles,
|
|
37
37
|
tsconfigRootDir,
|
|
38
|
-
typeChecked,
|
|
38
|
+
typeChecked: typescriptTypeChecked,
|
|
39
39
|
}),
|
|
40
|
+
...(typeChecked && !typescriptTypeChecked
|
|
41
|
+
? [
|
|
42
|
+
{
|
|
43
|
+
files: typescriptFiles,
|
|
44
|
+
languageOptions: {
|
|
45
|
+
parserOptions: {
|
|
46
|
+
projectService: true,
|
|
47
|
+
...(tsconfigRootDir && { tsconfigRootDir }),
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
name: "wowlab/react/typescript/project-service",
|
|
51
|
+
},
|
|
52
|
+
]
|
|
53
|
+
: []),
|
|
40
54
|
{
|
|
41
55
|
...typescriptConfig,
|
|
42
56
|
files: typescriptFiles,
|
|
@@ -8,15 +8,15 @@ export function typescript({ files = ["**/*.{ts,tsx,mts,cts}"], tsconfigRootDir,
|
|
|
8
8
|
? {
|
|
9
9
|
...config.languageOptions,
|
|
10
10
|
parserOptions: {
|
|
11
|
-
...(typeChecked
|
|
12
|
-
...(tsconfigRootDir
|
|
11
|
+
...(typeChecked && { projectService: true }),
|
|
12
|
+
...(tsconfigRootDir && { tsconfigRootDir }),
|
|
13
13
|
},
|
|
14
14
|
}
|
|
15
15
|
: undefined;
|
|
16
16
|
return {
|
|
17
17
|
...config,
|
|
18
18
|
files,
|
|
19
|
-
...(languageOptions
|
|
19
|
+
...(languageOptions && { languageOptions }),
|
|
20
20
|
name: config.name?.replace("typescript-eslint/", "wowlab/typescript/"),
|
|
21
21
|
};
|
|
22
22
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wowlab/eslint-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Composable ESLint flat configs for JavaScript and TypeScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -37,23 +37,23 @@
|
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"eslint": "^10.
|
|
41
|
-
"typescript": ">=
|
|
40
|
+
"eslint": "^10.4.0",
|
|
41
|
+
"typescript": ">=5.6.0 <6.1.0"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@eslint-community/eslint-plugin-eslint-comments": "
|
|
45
|
-
"@eslint-react/eslint-plugin": "
|
|
46
|
-
"@eslint/config-helpers": "
|
|
47
|
-
"@eslint/js": "
|
|
48
|
-
"@next/eslint-plugin-next": "16.
|
|
49
|
-
"@stylistic/eslint-plugin": "
|
|
50
|
-
"@tanstack/eslint-plugin-query": "
|
|
51
|
-
"eslint-plugin-perfectionist": "
|
|
52
|
-
"eslint-plugin-react-hooks": "
|
|
53
|
-
"eslint-plugin-sonarjs": "
|
|
54
|
-
"eslint-plugin-switch-case": "
|
|
55
|
-
"eslint-plugin-unicorn": "
|
|
56
|
-
"globals": "
|
|
57
|
-
"typescript-eslint": "8.
|
|
44
|
+
"@eslint-community/eslint-plugin-eslint-comments": "4.8.1",
|
|
45
|
+
"@eslint-react/eslint-plugin": "5.28.8",
|
|
46
|
+
"@eslint/config-helpers": "0.7.0",
|
|
47
|
+
"@eslint/js": "10.0.1",
|
|
48
|
+
"@next/eslint-plugin-next": "16.4.5",
|
|
49
|
+
"@stylistic/eslint-plugin": "5.10.0",
|
|
50
|
+
"@tanstack/eslint-plugin-query": "5.107.1",
|
|
51
|
+
"eslint-plugin-perfectionist": "5.13.0",
|
|
52
|
+
"eslint-plugin-react-hooks": "7.1.1",
|
|
53
|
+
"eslint-plugin-sonarjs": "4.2.2",
|
|
54
|
+
"eslint-plugin-switch-case": "4.0.0",
|
|
55
|
+
"eslint-plugin-unicorn": "79.0.0",
|
|
56
|
+
"globals": "17.14.0",
|
|
57
|
+
"typescript-eslint": "8.72.1"
|
|
58
58
|
}
|
|
59
59
|
}
|