@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 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 TypeScript and type-aware rules together.
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["flat/recommended"],
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": ["warn", { groups: CLASS_GROUPS }],
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": "warn",
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,2 @@
1
+ import type { ESLint } from "eslint";
2
+ export declare const wowlabPlugin: ESLint.Plugin;
@@ -0,0 +1,6 @@
1
+ import { functionPadding } from "./rules/function-padding.js";
2
+ export const wowlabPlugin = {
3
+ rules: {
4
+ "function-padding": functionPadding,
5
+ },
6
+ };
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "eslint";
2
+ export declare const functionPadding: Rule.RuleModule;
@@ -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
+ };
@@ -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": [
@@ -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[];
@@ -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 ? { projectService: true } : {}),
12
- ...(tsconfigRootDir ? { 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 ? { 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.1.2",
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.0.0",
41
- "typescript": ">=4.8.4 <6.1.0"
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": "^4.7.2",
45
- "@eslint-react/eslint-plugin": "^5.8.6",
46
- "@eslint/config-helpers": "^0.7.0",
47
- "@eslint/js": "^10.0.1",
48
- "@next/eslint-plugin-next": "16.2.9",
49
- "@stylistic/eslint-plugin": "^5.10.0",
50
- "@tanstack/eslint-plugin-query": "^5.100.14",
51
- "eslint-plugin-perfectionist": "^5.9.0",
52
- "eslint-plugin-react-hooks": "^7.1.1",
53
- "eslint-plugin-sonarjs": "^4.0.3",
54
- "eslint-plugin-switch-case": "^4.0.0",
55
- "eslint-plugin-unicorn": "^64.0.0",
56
- "globals": "^17.6.0",
57
- "typescript-eslint": "8.68.0"
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
  }