@aiou/eslint-config 3.1.1 → 3.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
@@ -10,7 +10,7 @@ Flat ESLint config with TypeScript, React, SSR, and more.
10
10
  - TypeScript, YAML, JSONC, Markdown, TOML support
11
11
  - Built-in React rules via `@eslint-react`, react-refresh, and SSR-safe linting
12
12
  - Auto-detected framework support (Next.js, Tailwind CSS)
13
- - Import sorting, unused import removal, and multi-line import enforcement
13
+ - Import sorting and unused import removal via `eslint-plugin-perfectionist`
14
14
  - Sorted keys for `package.json` and `tsconfig.json`
15
15
  - Visual linting progress reporter
16
16
 
@@ -96,7 +96,7 @@ The following plugins are enabled automatically when their packages are found in
96
96
  | Arrow parens | `as-needed` |
97
97
  | Curly | `all` |
98
98
  | File naming | `kebab-case` (via unicorn) |
99
- | Import sorting | side-effects`node:` builtins → packages → relative/aliasvirtual types |
99
+ | Import sorting | type-imports → builtins/externalsinternal → relative → side-effects (via `perfectionist`) |
100
100
 
101
101
  ## Included Plugins
102
102
 
@@ -108,8 +108,7 @@ The following plugins are enabled automatically when their packages are found in
108
108
  | `eslint-plugin-react-refresh` | React Refresh compatibility |
109
109
  | `@stylistic/eslint-plugin` | Formatting rules |
110
110
  | `eslint-plugin-import-x` | Import rules |
111
- | `eslint-plugin-simple-import-sort` | Import sorting |
112
- | `eslint-plugin-import-newlines` | Multi-line import enforcement |
111
+ | `eslint-plugin-perfectionist` | Import sorting, type sorting (union/intersection/named imports) |
113
112
  | `eslint-plugin-unused-imports` | Unused import/variable removal |
114
113
  | `eslint-plugin-unicorn` | Best practice rules |
115
114
  | `eslint-plugin-n` | Node.js rules |
package/dist/index.mjs CHANGED
@@ -2,9 +2,7 @@ import { FlatConfigComposer } from 'eslint-flat-config-utils';
2
2
  import { isPackageExists } from 'local-pkg';
3
3
  import pluginComments from '@eslint-community/eslint-plugin-eslint-comments';
4
4
  import ignoreGlobs from '@aiou/eslint-ignore';
5
- import pluginImportNewlines from 'eslint-plugin-import-newlines';
6
5
  import pluginImport from 'eslint-plugin-import-x';
7
- import pluginSimpleImportSort from 'eslint-plugin-simple-import-sort';
8
6
  import pluginUnsedImports from 'eslint-plugin-unused-imports';
9
7
  import pluginN from 'eslint-plugin-n';
10
8
  import pluginPromise from 'eslint-plugin-promise';
@@ -12,11 +10,13 @@ import globals from 'globals';
12
10
  import pluginJsonc from 'eslint-plugin-jsonc';
13
11
  import pluginMarkdown from '@eslint/markdown';
14
12
  import pluginNext from '@next/eslint-plugin-next';
13
+ import pluginPerfectionist from 'eslint-plugin-perfectionist';
15
14
  import pluginProgress from '@aiou/eslint-plugin-progress';
16
15
  import pluginReact from '@eslint-react/eslint-plugin';
17
16
  import pluginReactHooks from 'eslint-plugin-react-hooks';
18
17
  import pluginReactRefresh from 'eslint-plugin-react-refresh';
19
18
  import { configs } from 'eslint-plugin-regexp';
19
+ import storybookPlugin from 'eslint-plugin-storybook';
20
20
  import pluginStylistic from '@stylistic/eslint-plugin';
21
21
  import { createRequire } from 'node:module';
22
22
  import pluginToml from 'eslint-plugin-toml';
@@ -76,8 +76,6 @@ const imports = () => {
76
76
  {
77
77
  plugins: {
78
78
  import: pluginImport,
79
- "simple-import-sort": pluginSimpleImportSort,
80
- "import-newlines": pluginImportNewlines,
81
79
  "unused-imports": pluginUnsedImports
82
80
  },
83
81
  rules: {
@@ -85,29 +83,6 @@ const imports = () => {
85
83
  // off: controlled by import/order
86
84
  "import/order": "off",
87
85
  "sort-imports": "off",
88
- "simple-import-sort/imports": [
89
- "warn",
90
- {
91
- groups: [
92
- // Side effect imports.
93
- [String.raw`^\u0000`],
94
- // Node.js builtins prefixed with `node:`.
95
- ["^node:"],
96
- // Packages.
97
- // Things that start with a letter (or digit or underscore), or `@` followed by a letter.
98
- [String.raw`^@?\w`],
99
- // Relative imports.
100
- // Absolute imports and other imports such as `@/foo` or `~/foo`.
101
- // Anything not matched in another group.
102
- ["^", String.raw`^\.`, String.raw`^@/\w`, String.raw`^~/\w`],
103
- // Virtual modules prefixed with `virtual:` or `virtual-`, rollup & vite favor
104
- ["^virtual:", "^virtual-"],
105
- // Types
106
- ["^[^/\\.].*\0$", "^\\..*\0$"]
107
- ]
108
- }
109
- ],
110
- "simple-import-sort/exports": "off",
111
86
  "import/first": "error",
112
87
  "import/newline-after-import": "error",
113
88
  "import/no-duplicates": "error",
@@ -133,15 +108,6 @@ const imports = () => {
133
108
  args: "after-used",
134
109
  argsIgnorePattern: "^_"
135
110
  }
136
- ],
137
- // Enforce newlines inside named import
138
- "import-newlines/enforce": [
139
- "error",
140
- {
141
- items: 2,
142
- "max-len": 120,
143
- semi: false
144
- }
145
111
  ]
146
112
  }
147
113
  },
@@ -193,7 +159,8 @@ const imports = () => {
193
159
  // ignore require third packages in .eslintrc.* e.g. eslint-define-config
194
160
  `**/.eslintrc.${GLOB_SCRIPT_EXT}`,
195
161
  `**/**/eslint.config.${GLOB_SCRIPT_EXT}`,
196
- "**/{vite,esbuild,rollup,webpack,rspack}.ts"
162
+ "**/{vite,esbuild,rollup,webpack,rspack}.ts",
163
+ "**/vitest.setup.*"
197
164
  ],
198
165
  rules: {
199
166
  "import/no-extraneous-dependencies": "off"
@@ -753,7 +720,8 @@ const markdown = () => {
753
720
  "import/no-anonymous-default-export": "off",
754
721
  "react-refresh/only-export-components": "off",
755
722
  "react/jsx-no-undef": "off",
756
- "unicorn/filename-case": "off"
723
+ "unicorn/filename-case": "off",
724
+ "perfectionist/sort-imports": "off"
757
725
  }
758
726
  }
759
727
  ];
@@ -775,6 +743,46 @@ const next = () => {
775
743
  return config;
776
744
  };
777
745
 
746
+ const perfectionist = () => {
747
+ return [
748
+ {
749
+ plugins: {
750
+ perfectionist: pluginPerfectionist
751
+ },
752
+ rules: {
753
+ "perfectionist/sort-imports": [
754
+ "warn",
755
+ {
756
+ type: "natural",
757
+ order: "asc",
758
+ newlinesBetween: "ignore",
759
+ groups: [
760
+ "type-import",
761
+ ["value-builtin", "value-external"],
762
+ "type-internal",
763
+ "value-internal",
764
+ ["type-parent", "type-sibling", "type-index"],
765
+ ["value-parent", "value-sibling", "value-index"],
766
+ "side-effect",
767
+ "unknown"
768
+ ],
769
+ customGroups: [
770
+ {
771
+ groupName: "virtual-modules",
772
+ elementNamePattern: ["^virtual:", "^virtual-"]
773
+ }
774
+ ],
775
+ internalPattern: ["^~/.+", "^@/.+", "^#.+"]
776
+ }
777
+ ],
778
+ "perfectionist/sort-union-types": ["error", { type: "natural", order: "asc" }],
779
+ "perfectionist/sort-intersection-types": ["error", { type: "natural", order: "asc" }],
780
+ "perfectionist/sort-named-imports": ["warn", { type: "natural", order: "asc" }]
781
+ }
782
+ }
783
+ ];
784
+ };
785
+
778
786
  const progress = () => {
779
787
  const config = [
780
788
  {
@@ -823,33 +831,28 @@ const react = () => {
823
831
  react: pluginReact,
824
832
  "react-dom": pluginReact,
825
833
  "react-hooks": pluginReactHooks,
826
- "react-hooks-extra": pluginReact,
827
834
  "react-naming-convention": pluginReact,
828
835
  "react-refresh": pluginReactRefresh,
829
836
  "react-web-api": pluginReact
830
837
  },
831
838
  rules: {
832
839
  ...renameRules(pluginReact.configs["recommended-typescript"].rules, { "@eslint-react": "react" }),
833
- ...renameRules(pluginReact.configs.dom.rules, { "@eslint-react": "react-dom" }),
834
- ...renameRules(pluginReact.configs.x.rules, { "@eslint-react": "react-hooks-extra" }),
835
- ...renameRules(pluginReact.configs.x.rules, { "@eslint-react": "react-naming-convention" }),
836
- ...renameRules(pluginReact.configs["web-api"].rules, { "@eslint-react": "react-web-api" }),
837
840
  ...pluginReactHooks.configs.flat.recommended.rules,
838
841
  "react/no-nested-component-definitions": "warn",
839
- "react-dom/no-unknown-property": "off",
842
+ "react-dom/dom-no-unknown-property": "off",
840
843
  "react-refresh/only-export-components": "warn"
841
844
  }
842
845
  },
843
846
  {
844
847
  files: ["src/components/**/*.{ts,tsx}"],
845
848
  rules: {
846
- "react-naming-convention/filename": ["warn", { rule: "PascalCase" }]
849
+ "unicorn/filename-case": ["warn", { case: "pascalCase" }]
847
850
  }
848
851
  },
849
852
  {
850
853
  files: ["src/hooks/**/use*.{ts,tsx}"],
851
854
  rules: {
852
- "react-naming-convention/filename": ["warn", { rule: "kebab-case" }]
855
+ "unicorn/filename-case": ["warn", { case: "kebabCase" }]
853
856
  }
854
857
  },
855
858
  {
@@ -974,6 +977,13 @@ const regexp = () => {
974
977
  return config;
975
978
  };
976
979
 
980
+ const storybook = () => {
981
+ const config = [
982
+ ...storybookPlugin.configs["flat/recommended"]
983
+ ];
984
+ return config;
985
+ };
986
+
977
987
  const stylistic = () => {
978
988
  const config = pluginStylistic.configs.customize({
979
989
  indent: 2,
@@ -1180,10 +1190,7 @@ const typescript = () => {
1180
1190
  ],
1181
1191
  // Limit `interface` define object types, users could override with *.d.ts declare
1182
1192
  "@typescript-eslint/consistent-type-definitions": ["error", "interface"],
1183
- "@typescript-eslint/prefer-ts-expect-error": "warn",
1184
1193
  "@typescript-eslint/default-param-last": "error",
1185
- // Sort type S = A | B
1186
- "@typescript-eslint/sort-type-constituents": "error",
1187
1194
  "@typescript-eslint/unbound-method": "off",
1188
1195
  "@typescript-eslint/prefer-for-of": "error",
1189
1196
  // When .ts files compiled to .mjs, will throw require is not found
@@ -1227,8 +1234,7 @@ const typescript = () => {
1227
1234
  "error",
1228
1235
  { functions: false, classes: false, variables: true }
1229
1236
  ],
1230
- "no-loss-of-precision": "off",
1231
- "@typescript-eslint/no-loss-of-precision": "error",
1237
+ "no-loss-of-precision": "error",
1232
1238
  "no-unused-expressions": "off",
1233
1239
  "@typescript-eslint/no-unused-expressions": ["error", {
1234
1240
  allowShortCircuit: true,
@@ -1236,19 +1242,13 @@ const typescript = () => {
1236
1242
  allowTaggedTemplates: true
1237
1243
  }],
1238
1244
  // off
1239
- "@typescript-eslint/camelcase": "off",
1240
1245
  "@typescript-eslint/explicit-function-return-type": "off",
1241
1246
  "@typescript-eslint/explicit-member-accessibility": "off",
1242
1247
  "@typescript-eslint/no-explicit-any": "off",
1243
- "@typescript-eslint/no-parameter-properties": "off",
1244
- "@typescript-eslint/no-empty-interface": "off",
1245
- "@typescript-eslint/ban-ts-ignore": "off",
1246
1248
  "@typescript-eslint/no-empty-function": "off",
1247
1249
  "@typescript-eslint/no-non-null-assertion": "off",
1248
1250
  "@typescript-eslint/explicit-module-boundary-types": "off",
1249
- "@typescript-eslint/ban-types": "off",
1250
1251
  "@typescript-eslint/no-namespace": "off",
1251
- "@typescript-eslint/no-var-requires": "off",
1252
1252
  // https://www.npmjs.com/package/eslint-plugin-unused-imports
1253
1253
  "@typescript-eslint/no-unused-vars": "off",
1254
1254
  "no-void": ["error", { allowAsStatement: true }],
@@ -1292,6 +1292,8 @@ const unicorn = () => {
1292
1292
  "unicorn/prefer-string-starts-ends-with": "error",
1293
1293
  "unicorn/prefer-type-error": "error",
1294
1294
  "unicorn/throw-new-error": "error",
1295
+ "unicorn/prevent-abbreviations": "off",
1296
+ "unicorn/no-null": "off",
1295
1297
  "unicorn/filename-case": ["error", {
1296
1298
  case: "kebabCase",
1297
1299
  ignore: [/^[A-Z]+\.md$/, /^[A-Z]+\.yml$/, /^\.?[A-Z]/, /^Dockerfile$/]
@@ -1328,7 +1330,8 @@ const presetJavascript = [
1328
1330
  ...javascript(),
1329
1331
  ...comments(),
1330
1332
  ...imports(),
1331
- ...unicorn()
1333
+ ...unicorn(),
1334
+ ...perfectionist()
1332
1335
  ];
1333
1336
  const presetTypescript = [
1334
1337
  ...presetJavascript,
@@ -1345,18 +1348,20 @@ const presetDefault = [
1345
1348
  ...react(),
1346
1349
  ...stylistic(),
1347
1350
  ...presetLangsExtensions,
1348
- ...progress()
1351
+ ...progress(),
1352
+ ...storybook()
1349
1353
  ];
1350
1354
  const all = [
1351
1355
  ...presetDefault,
1352
1356
  ...tailwindcss(),
1353
1357
  ...next(),
1354
1358
  ...ssrReact(),
1355
- ...regexp()
1359
+ ...regexp(),
1360
+ ...storybook()
1356
1361
  ];
1357
- const aiou = ({ ssr = true, regexp: regexp$1 = true } = { ssr: true, regexp: true }, ...userConfigs) => {
1362
+ const aiou = ({ ssr = true, regexp: regexp$1 = true, tailwindcss: enableTailwindcss = true } = {}, ...userConfigs) => {
1358
1363
  const configs = [...presetDefault];
1359
- if (isPackageExists("tailwindcss")) {
1364
+ if (enableTailwindcss && isPackageExists("tailwindcss")) {
1360
1365
  configs.push(...tailwindcss());
1361
1366
  }
1362
1367
  if (isPackageExists("next")) {
@@ -0,0 +1,2 @@
1
+ import type { Config } from '../type';
2
+ export declare const perfectionist: () => Config[];
@@ -0,0 +1,2 @@
1
+ import type { Config } from '../type';
2
+ export declare const storybook: () => Config[];
package/dts/globs.d.ts CHANGED
@@ -17,3 +17,5 @@ export declare const GLOB_MARKDOWN = "**/*.md";
17
17
  export declare const GLOB_VUE = "**/*.vue";
18
18
  export declare const GLOB_YAML = "**/*.y?(a)ml";
19
19
  export declare const GLOB_WORKFLOW_YAML = "**/.github/**/*.y?(a)ml";
20
+ export declare const GLOB_STORYBOOK_STORIES = "**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)";
21
+ export declare const GLOB_STORYBOOK_MAIN = ".storybook/main.@(js|cjs|mjs|ts)";
package/dts/index.d.ts CHANGED
@@ -1,12 +1,13 @@
1
- import { FlatConfigComposer } from 'eslint-flat-config-utils';
2
1
  import type { Linter } from 'eslint';
3
2
  import type { Arrayable, Awaitable } from 'eslint-flat-config-utils';
3
+ import { FlatConfigComposer } from 'eslint-flat-config-utils';
4
4
  import type { Config } from './type';
5
5
  import type { ConfigNames } from './typegen';
6
6
  export declare const all: Config[];
7
7
  interface Options {
8
8
  ssr?: boolean;
9
9
  regexp?: boolean;
10
+ tailwindcss?: boolean;
10
11
  }
11
- export declare const aiou: ({ ssr, regexp }?: Options, ...userConfigs: Awaitable<Arrayable<Config> | FlatConfigComposer<any, any> | Linter.Config[]>[]) => FlatConfigComposer<Config, ConfigNames>;
12
+ export declare const aiou: ({ ssr, regexp, tailwindcss: enableTailwindcss }?: Options, ...userConfigs: Awaitable<Arrayable<Config> | FlatConfigComposer<any, any> | Linter.Config[]>[]) => FlatConfigComposer<Config, ConfigNames>;
12
13
  export {};