@slango.configs/eslint 1.0.30 → 1.1.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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @slango.configs/eslint@1.0.30 lint /home/runner/work/slango/slango/configs/eslint
2
+ > @slango.configs/eslint@1.1.0 lint /home/runner/work/slango/slango/configs/eslint
3
3
  > eslint . --max-warnings 0
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @slango.configs/eslint
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ce8f03a: feat: allow configurable perfectionist rule strictness levels via options object with validation.
8
+
9
+ ## 1.0.31
10
+
11
+ ### Patch Changes
12
+
13
+ - 450ff4b: Dependencies bump
14
+
3
15
  ## 1.0.30
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -34,6 +34,23 @@ Unless you want to override the default configuration, you can simply extend the
34
34
  export { default } from '@slango.configs/eslint/{preset-you-wish-to-use}.js';
35
35
  ```
36
36
 
37
+ ### Perfectionist rule strictness
38
+
39
+ Perfectionist sorting rules can be configured with three levels of strictness:
40
+
41
+ - `off` – disables all perfectionist sorting rules
42
+ - `relaxed` – only enforces module sorting (default)
43
+ - `strict` – enforces sorting for modules, enums, object types and objects
44
+
45
+ To use a custom level, import the corresponding creator and pass an options object. Passing an unknown option or an invalid level throws an error:
46
+
47
+ ```js
48
+ // eslint.config.js
49
+ import { createTypescriptConfig } from '@slango.configs/eslint/typescript.js';
50
+
51
+ export default createTypescriptConfig({ perfectionist: 'strict' });
52
+ ```
53
+
37
54
  ## Things to take into account (and possibly review)
38
55
 
39
56
  - At the time of writing eslint-config-next is not compatible with eslint 9, thus @eslint/compat and @eslint/eslintrc are used
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slango.configs/eslint",
3
- "version": "1.0.30",
3
+ "version": "1.1.0",
4
4
  "private": false,
5
5
  "description": "Slango eslint configs",
6
6
  "type": "module",
@@ -12,8 +12,8 @@
12
12
  "@eslint/compat": "1.3.2",
13
13
  "@eslint/eslintrc": "3.3.1",
14
14
  "@eslint/js": "9.33.0",
15
- "@next/eslint-plugin-next": "15.4.6",
16
- "eslint-config-next": "15.4.6",
15
+ "@next/eslint-plugin-next": "15.4.7",
16
+ "eslint-config-next": "15.4.7",
17
17
  "eslint-config-prettier": "10.1.8",
18
18
  "eslint-import-resolver-typescript": "4.4.4",
19
19
  "eslint-plugin-import-x": "4.16.1",
@@ -23,7 +23,7 @@
23
23
  "eslint-plugin-react-refresh": "0.4.20",
24
24
  "eslint-plugin-regexp": "2.10.0",
25
25
  "globals": "16.3.0",
26
- "typescript-eslint": "8.39.1"
26
+ "typescript-eslint": "8.40.0"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "eslint": "^9.33.0"
package/src/common.js CHANGED
@@ -23,11 +23,55 @@ export const commonConfigs = (files) =>
23
23
  regexPluginConfigs['flat/recommended'],
24
24
  ].map((config) => ({ ...config, files }));
25
25
 
26
- export const commonRules = {
27
- 'perfectionist/sort-enums': 'off',
28
- 'perfectionist/sort-modules': ['error', { partitionByNewLine: true }],
29
- 'perfectionist/sort-object-types': 'off',
30
- 'perfectionist/sort-objects': 'off',
26
+ const sortModulesRule = ['error', { partitionByNewLine: true }];
27
+
28
+ export const perfectionistLevels = {
29
+ off: {
30
+ 'perfectionist/sort-enums': 'off',
31
+ 'perfectionist/sort-modules': 'off',
32
+ 'perfectionist/sort-object-types': 'off',
33
+ 'perfectionist/sort-objects': 'off',
34
+ },
35
+ relaxed: {
36
+ 'perfectionist/sort-enums': 'off',
37
+ 'perfectionist/sort-modules': sortModulesRule,
38
+ 'perfectionist/sort-object-types': 'off',
39
+ 'perfectionist/sort-objects': 'off',
40
+ },
41
+ strict: {
42
+ 'perfectionist/sort-enums': 'error',
43
+ 'perfectionist/sort-modules': sortModulesRule,
44
+ 'perfectionist/sort-object-types': 'error',
45
+ 'perfectionist/sort-objects': 'error',
46
+ },
47
+ };
48
+
49
+ const defaultOptions = { perfectionist: 'relaxed' };
50
+
51
+ export const normalizeOptions = (options = {}) => {
52
+ if (options === null || typeof options !== 'object' || Array.isArray(options)) {
53
+ throw new Error('options must be an object');
54
+ }
55
+
56
+ const { perfectionist = defaultOptions.perfectionist, ...rest } = options;
57
+
58
+ if (!Object.hasOwn(perfectionistLevels, perfectionist)) {
59
+ throw new Error(
60
+ `Invalid perfectionist level "${perfectionist}". Expected one of: ${Object.keys(perfectionistLevels).join(', ')}`,
61
+ );
62
+ }
63
+
64
+ const unknown = Object.keys(rest);
65
+ if (unknown.length > 0) {
66
+ throw new Error(`Unknown option(s): ${unknown.join(', ')}`);
67
+ }
68
+
69
+ return { perfectionist };
70
+ };
71
+
72
+ export const commonRules = (options) => {
73
+ const { perfectionist } = normalizeOptions(options);
74
+ return perfectionistLevels[perfectionist];
31
75
  };
32
76
 
33
77
  export const typescriptConfigs = (files) => [
@@ -1,35 +1,43 @@
1
1
  import globals from 'globals';
2
2
 
3
- import { commonConfigs, commonRules, globs, ignorePatterns } from '../common.js';
3
+ import { commonConfigs, commonRules, globs, ignorePatterns, normalizeOptions } from '../common.js';
4
4
 
5
- export const baseJavascriptConfig = {
6
- files: [globs.javascript],
7
- languageOptions: {
8
- globals: {
9
- ...globals.node,
5
+ export const baseJavascriptConfig = (options = {}) => {
6
+ const opts = normalizeOptions(options);
7
+ return {
8
+ files: [globs.javascript],
9
+ languageOptions: {
10
+ globals: {
11
+ ...globals.node,
12
+ },
13
+ parserOptions: {
14
+ ecmaVersion: 'latest',
15
+ },
10
16
  },
11
- parserOptions: {
12
- ecmaVersion: 'latest',
17
+ name: '@slango.configs/eslint/javascript-node',
18
+ rules: {
19
+ 'import-x/first': 'error',
20
+ 'import-x/no-anonymous-default-export': 'error',
21
+ ...commonRules(opts),
13
22
  },
14
- },
15
- name: '@slango.configs/eslint/javascript-node',
16
- rules: {
17
- 'import-x/first': 'error',
18
- 'import-x/no-anonymous-default-export': 'error',
19
- ...commonRules,
20
- },
21
- settings: {
22
- // eslint-import-resolver-node does not seem to play well with monorepo/pattern exports
23
- 'import-x/resolver': {
24
- typescript: true,
23
+ settings: {
24
+ // eslint-import-resolver-node does not seem to play well with monorepo/pattern exports
25
+ 'import-x/resolver': {
26
+ typescript: true,
27
+ },
25
28
  },
26
- },
29
+ };
27
30
  };
28
31
 
29
- const javascriptNode = [
30
- ...commonConfigs(globs.javascript),
31
- baseJavascriptConfig,
32
- { ignores: ignorePatterns },
33
- ];
32
+ export const createJavascriptNodeConfig = (options = {}) => {
33
+ const opts = normalizeOptions(options);
34
+ return [
35
+ ...commonConfigs(globs.javascript),
36
+ baseJavascriptConfig(opts),
37
+ { ignores: ignorePatterns },
38
+ ];
39
+ };
40
+
41
+ const javascriptNode = createJavascriptNodeConfig();
34
42
 
35
43
  export default javascriptNode;
@@ -1,27 +1,39 @@
1
1
  import globals from 'globals';
2
2
  import tsEslint from 'typescript-eslint';
3
3
 
4
- import { globs, typescriptConfigs } from '../common.js';
5
- import javascriptNode from './javascript-node.js';
4
+ import { globs, normalizeOptions, typescriptConfigs } from '../common.js';
5
+ import { createJavascriptNodeConfig } from './javascript-node.js';
6
6
  import { baseTypescriptConfig } from './typescript.js';
7
7
 
8
- export const browserTypescriptBrowserConfig = {
9
- ...baseTypescriptConfig,
10
- languageOptions: {
11
- ...baseTypescriptConfig.languageOptions,
12
- ecmaVersion: 2022,
13
- globals: {
14
- ...baseTypescriptConfig.languageOptions.globals,
15
- ...globals['2022'],
16
- ...globals.browser,
8
+ export const browserTypescriptBrowserConfig = (options = {}) => {
9
+ const opts = normalizeOptions(options);
10
+ const base = baseTypescriptConfig(opts);
11
+ return {
12
+ ...base,
13
+ languageOptions: {
14
+ ...base.languageOptions,
15
+ ecmaVersion: 2022,
16
+ globals: {
17
+ ...base.languageOptions.globals,
18
+ ...globals['2022'],
19
+ ...globals.browser,
20
+ },
17
21
  },
18
- },
19
- name: '@slango.configs/eslint/typescript-browser',
22
+ name: '@slango.configs/eslint/typescript-browser',
23
+ };
20
24
  };
21
25
 
22
- const typescriptBrowserConfig = [
23
- ...javascriptNode,
24
- ...tsEslint.config(...typescriptConfigs(globs.typescript), browserTypescriptBrowserConfig),
25
- ];
26
+ export const createTypescriptBrowserConfig = (options = {}) => {
27
+ const opts = normalizeOptions(options);
28
+ return [
29
+ ...createJavascriptNodeConfig(opts),
30
+ ...tsEslint.config(
31
+ ...typescriptConfigs(globs.typescript),
32
+ browserTypescriptBrowserConfig(opts),
33
+ ),
34
+ ];
35
+ };
36
+
37
+ const typescriptBrowserConfig = createTypescriptBrowserConfig();
26
38
 
27
39
  export default typescriptBrowserConfig;
@@ -1,25 +1,37 @@
1
1
  import globals from 'globals';
2
2
  import tsEslint from 'typescript-eslint';
3
3
 
4
- import { globs, typescriptConfigs } from '../common.js';
5
- import javascriptNode from './javascript-node.js';
4
+ import { globs, normalizeOptions, typescriptConfigs } from '../common.js';
5
+ import { createJavascriptNodeConfig } from './javascript-node.js';
6
6
  import { browserTypescriptBrowserConfig } from './typescript-browser.js';
7
7
 
8
- export const isomorphicTypescriptBrowserConfig = {
9
- ...browserTypescriptBrowserConfig,
10
- languageOptions: {
11
- ...browserTypescriptBrowserConfig.languageOptions,
12
- globals: {
13
- ...browserTypescriptBrowserConfig.languageOptions.globals,
14
- ...globals.node,
8
+ export const isomorphicTypescriptBrowserConfig = (options = {}) => {
9
+ const opts = normalizeOptions(options);
10
+ const browserConfig = browserTypescriptBrowserConfig(opts);
11
+ return {
12
+ ...browserConfig,
13
+ languageOptions: {
14
+ ...browserConfig.languageOptions,
15
+ globals: {
16
+ ...browserConfig.languageOptions.globals,
17
+ ...globals.node,
18
+ },
15
19
  },
16
- },
17
- name: '@slango.configs/eslint/typescript-isomorphic',
20
+ name: '@slango.configs/eslint/typescript-isomorphic',
21
+ };
18
22
  };
19
23
 
20
- const typescriptIsomorphicConfig = [
21
- ...javascriptNode,
22
- ...tsEslint.config(...typescriptConfigs(globs.typescript), isomorphicTypescriptBrowserConfig),
23
- ];
24
+ export const createTypescriptIsomorphicConfig = (options = {}) => {
25
+ const opts = normalizeOptions(options);
26
+ return [
27
+ ...createJavascriptNodeConfig(opts),
28
+ ...tsEslint.config(
29
+ ...typescriptConfigs(globs.typescript),
30
+ isomorphicTypescriptBrowserConfig(opts),
31
+ ),
32
+ ];
33
+ };
34
+
35
+ const typescriptIsomorphicConfig = createTypescriptIsomorphicConfig();
24
36
 
25
37
  export default typescriptIsomorphicConfig;
@@ -5,9 +5,9 @@ import path from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import tsEslint from 'typescript-eslint';
7
7
 
8
- import { globs, typescriptConfigs } from '../common.js';
8
+ import { globs, normalizeOptions, typescriptConfigs } from '../common.js';
9
9
  import { getTsConfigFile } from '../utils.js';
10
- import javascriptNode from './javascript-node.js';
10
+ import { createJavascriptNodeConfig } from './javascript-node.js';
11
11
  import { browserTypescriptBrowserConfig } from './typescript-browser.js';
12
12
 
13
13
  const project = getTsConfigFile();
@@ -31,26 +31,33 @@ const patchedConfig = fixupConfigRules([...compat.extends('next/core-web-vitals'
31
31
  }),
32
32
  );
33
33
 
34
- const typescriptNextConfig = [
35
- ...javascriptNode,
36
- ...tsEslint.config(
37
- ...typescriptConfigs(globs.typescript),
38
- {
39
- ...browserTypescriptBrowserConfig,
40
- languageOptions: {
41
- ...browserTypescriptBrowserConfig.languageOptions,
42
- globals: {
43
- ...browserTypescriptBrowserConfig.languageOptions.globals,
44
- process: true,
45
- NodeJS: 'readonly',
34
+ export const createTypescriptNextConfig = (options = {}) => {
35
+ const opts = normalizeOptions(options);
36
+ const browserConfig = browserTypescriptBrowserConfig(opts);
37
+
38
+ return [
39
+ ...createJavascriptNodeConfig(opts),
40
+ ...tsEslint.config(
41
+ ...typescriptConfigs(globs.typescript),
42
+ {
43
+ ...browserConfig,
44
+ languageOptions: {
45
+ ...browserConfig.languageOptions,
46
+ globals: {
47
+ ...browserConfig.languageOptions.globals,
48
+ process: true,
49
+ NodeJS: 'readonly',
50
+ },
46
51
  },
47
52
  },
48
- },
49
- ...patchedConfig,
50
- {
51
- ignores: ['.next/*'],
52
- },
53
- ),
54
- ];
53
+ ...patchedConfig,
54
+ {
55
+ ignores: ['.next/*'],
56
+ },
57
+ ),
58
+ ];
59
+ };
60
+
61
+ const typescriptNextConfig = createTypescriptNextConfig();
55
62
 
56
63
  export default typescriptNextConfig;
@@ -1,25 +1,31 @@
1
1
  import globals from 'globals';
2
2
  import tsEslint from 'typescript-eslint';
3
3
 
4
- import { globs, typescriptConfigs } from '../common.js';
5
- import javascriptNode from './javascript-node.js';
4
+ import { globs, normalizeOptions, typescriptConfigs } from '../common.js';
5
+ import { createJavascriptNodeConfig } from './javascript-node.js';
6
6
  import { baseTypescriptConfig } from './typescript.js';
7
7
 
8
- const nodeTypescriptConfig = {
9
- ...baseTypescriptConfig,
10
- languageOptions: {
11
- ...baseTypescriptConfig.languageOptions,
12
- globals: {
13
- ...baseTypescriptConfig.languageOptions.globals,
14
- ...globals.node,
8
+ export const createTypescriptNodeConfig = (options = {}) => {
9
+ const opts = normalizeOptions(options);
10
+ const base = baseTypescriptConfig(opts);
11
+ const nodeTypescriptConfig = {
12
+ ...base,
13
+ languageOptions: {
14
+ ...base.languageOptions,
15
+ globals: {
16
+ ...base.languageOptions.globals,
17
+ ...globals.node,
18
+ },
15
19
  },
16
- },
17
- name: '@slango.configs/eslint/typescript-node',
20
+ name: '@slango.configs/eslint/typescript-node',
21
+ };
22
+
23
+ return [
24
+ ...createJavascriptNodeConfig(opts),
25
+ ...tsEslint.config(...typescriptConfigs(globs.typescript), nodeTypescriptConfig),
26
+ ];
18
27
  };
19
28
 
20
- const typescriptNodeConfig = [
21
- ...javascriptNode,
22
- ...tsEslint.config(...typescriptConfigs(globs.typescript), nodeTypescriptConfig),
23
- ];
29
+ const typescriptNodeConfig = createTypescriptNodeConfig();
24
30
 
25
31
  export default typescriptNodeConfig;
@@ -3,28 +3,40 @@ import reactRefresh from 'eslint-plugin-react-refresh';
3
3
  import globals from 'globals';
4
4
  import tsEslint from 'typescript-eslint';
5
5
 
6
- import { globs, typescriptConfigs } from '../common.js';
7
- import javascriptNode from './javascript-node.js';
6
+ import { globs, normalizeOptions, typescriptConfigs } from '../common.js';
7
+ import { createJavascriptNodeConfig } from './javascript-node.js';
8
8
  import { browserTypescriptBrowserConfig } from './typescript-browser.js';
9
9
  import { baseTypescriptConfig } from './typescript.js';
10
10
 
11
- export const reactTypescriptBrowserConfig = {
12
- ...baseTypescriptConfig,
13
- languageOptions: {
14
- ...baseTypescriptConfig.languageOptions,
15
- globals: {
16
- ...baseTypescriptConfig.languageOptions.globals,
17
- ...globals.browser,
11
+ export const reactTypescriptBrowserConfig = (options = {}) => {
12
+ const opts = normalizeOptions(options);
13
+ const base = baseTypescriptConfig(opts);
14
+ return {
15
+ ...base,
16
+ languageOptions: {
17
+ ...base.languageOptions,
18
+ globals: {
19
+ ...base.languageOptions.globals,
20
+ ...globals.browser,
21
+ },
18
22
  },
19
- },
20
- name: '@slango.configs/eslint/typescript-react',
23
+ name: '@slango.configs/eslint/typescript-react',
24
+ };
21
25
  };
22
26
 
23
- const typescriptReactConfig = [
24
- ...javascriptNode,
25
- reactHooks.configs['recommended-latest'],
26
- reactRefresh.configs.recommended,
27
- ...tsEslint.config(...typescriptConfigs(globs.typescript), browserTypescriptBrowserConfig),
28
- ];
27
+ export const createTypescriptReactConfig = (options = {}) => {
28
+ const opts = normalizeOptions(options);
29
+ return [
30
+ ...createJavascriptNodeConfig(opts),
31
+ reactHooks.configs['recommended-latest'],
32
+ reactRefresh.configs.recommended,
33
+ ...tsEslint.config(
34
+ ...typescriptConfigs(globs.typescript),
35
+ browserTypescriptBrowserConfig(opts),
36
+ ),
37
+ ];
38
+ };
39
+
40
+ const typescriptReactConfig = createTypescriptReactConfig();
29
41
 
30
42
  export default typescriptReactConfig;
@@ -1,36 +1,51 @@
1
1
  import { dirname } from 'node:path';
2
2
  import tsEslint from 'typescript-eslint';
3
3
 
4
- import { commonConfigs, commonRules, globs, ignorePatterns, typescriptConfigs } from '../common.js';
4
+ import {
5
+ commonConfigs,
6
+ commonRules,
7
+ globs,
8
+ ignorePatterns,
9
+ normalizeOptions,
10
+ typescriptConfigs,
11
+ } from '../common.js';
5
12
  import { getTsConfigFile } from '../utils.js';
6
- import javascriptNode, { baseJavascriptConfig } from './javascript-node.js';
13
+ import { baseJavascriptConfig, createJavascriptNodeConfig } from './javascript-node.js';
7
14
 
8
15
  const project = getTsConfigFile();
9
16
 
10
- export const baseTypescriptConfig = {
11
- ...baseJavascriptConfig,
12
- extends: [...commonConfigs(globs.typescript)],
13
- files: [globs.typescript],
14
- languageOptions: {
15
- parserOptions: {
16
- project,
17
- tsconfigRootDir: dirname(project),
17
+ export const baseTypescriptConfig = (options = {}) => {
18
+ const opts = normalizeOptions(options);
19
+ return {
20
+ ...baseJavascriptConfig(opts),
21
+ extends: [...commonConfigs(globs.typescript)],
22
+ files: [globs.typescript],
23
+ languageOptions: {
24
+ parserOptions: {
25
+ project,
26
+ tsconfigRootDir: dirname(project),
27
+ },
18
28
  },
19
- },
20
- name: '@slango.configs/eslint/typescript',
21
- rules: {
22
- // Allow destructuring (to remove unused properties)
23
- '@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }],
24
- // Disable as @typescript-eslint/no-unused-vars is used (and no-unused-vars has issues with enums)
25
- 'no-unused-vars': 'off',
26
- ...commonRules,
27
- },
29
+ name: '@slango.configs/eslint/typescript',
30
+ rules: {
31
+ // Allow destructuring (to remove unused properties)
32
+ '@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }],
33
+ // Disable as @typescript-eslint/no-unused-vars is used (and no-unused-vars has issues with enums)
34
+ 'no-unused-vars': 'off',
35
+ ...commonRules(opts),
36
+ },
37
+ };
38
+ };
39
+
40
+ export const createTypescriptConfig = (options = {}) => {
41
+ const opts = normalizeOptions(options);
42
+ return [
43
+ ...createJavascriptNodeConfig(opts),
44
+ ...tsEslint.config(...typescriptConfigs(globs.typescript), baseTypescriptConfig(opts)),
45
+ { ignores: ignorePatterns },
46
+ ];
28
47
  };
29
48
 
30
- const typescriptConfig = [
31
- ...javascriptNode,
32
- ...tsEslint.config(...typescriptConfigs(globs.typescript), baseTypescriptConfig),
33
- { ignores: ignorePatterns },
34
- ];
49
+ const typescriptConfig = createTypescriptConfig();
35
50
 
36
51
  export default typescriptConfig;