Skip to content

Commit 6d956e0

Browse files
committed
feat: switch to exporting arrays to allow multiple configuration objects
1 parent 88ec9f6 commit 6d956e0

7 files changed

Lines changed: 533 additions & 517 deletions

File tree

@typescript-eslint.js

Lines changed: 152 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Generates an ESLint config for TypeScript, based on the Ackama style guide
33
*
4-
* @return {import('eslint').Linter.FlatConfig|import('eslint').Linter.LegacyConfig}
4+
* @return {import('eslint').Linter.FlatConfig[]|import('eslint').Linter.LegacyConfig}
55
*/
66
const generateConfig = () => {
77
if (process.env.ESLINT_USE_FLAT_CONFIG !== 'false') {
@@ -13,156 +13,161 @@ const generateConfig = () => {
1313
const pluginPrettierRecommended = require('eslint-plugin-prettier/recommended');
1414
/* eslint-enable n/global-require */
1515

16-
/** @type {import('eslint').Linter.FlatConfig} */
17-
const config = {
18-
languageOptions: { parser: parserTypeScriptESLint },
19-
plugins: {
20-
'@typescript-eslint': pluginTypeScriptESLint,
21-
'@stylistic/ts': pluginStylisticTS,
22-
'prettier': pluginPrettier
23-
},
24-
rules: {
25-
...pluginTypeScriptESLint.configs['recommended-type-checked'].rules,
26-
...pluginTypeScriptESLint.configs['stylistic-type-checked'].rules,
27-
...pluginPrettierRecommended.rules,
16+
/** @type {import('eslint').Linter.FlatConfig[]} */
17+
const config = [
18+
{
19+
languageOptions: { parser: parserTypeScriptESLint },
20+
plugins: {
21+
'@typescript-eslint': pluginTypeScriptESLint,
22+
'@stylistic/ts': pluginStylisticTS,
23+
'prettier': pluginPrettier
24+
},
25+
rules: {
26+
...pluginTypeScriptESLint.configs['recommended-type-checked'].rules,
27+
...pluginTypeScriptESLint.configs['stylistic-type-checked'].rules,
28+
...pluginPrettierRecommended.rules,
2829

29-
// explicitly (re)enable this as it's disabled by eslint-config-prettier
30-
// and its likely our standard JS config will be used alongside this one
31-
'curly': 'error',
30+
// explicitly (re)enable this as it's disabled by eslint-config-prettier
31+
// and its likely our standard JS config will be used alongside this one
32+
'curly': 'error',
3233

33-
'@typescript-eslint/array-type': ['error', { default: 'array-simple' }],
34-
'@typescript-eslint/default-param-last': 'error',
35-
'@typescript-eslint/explicit-member-accessibility': 'error',
36-
'@typescript-eslint/explicit-module-boundary-types': 'error',
37-
'@stylistic/ts/lines-between-class-members': [
38-
'error',
39-
'always',
40-
{ exceptAfterSingleLine: true }
41-
],
42-
'@typescript-eslint/naming-convention': [
43-
'error',
44-
{
45-
selector: 'default',
46-
format: ['camelCase', 'PascalCase', 'UPPER_CASE']
47-
},
48-
{ selector: 'property', format: null },
49-
{ selector: 'typeLike', format: ['PascalCase'] },
50-
{
51-
selector: 'typeParameter',
52-
format: ['PascalCase'],
53-
custom: {
54-
match: true,
55-
regex: /^T([A-Z][a-zA-Z]+)$|^[A-Z]$/u.source
34+
'@typescript-eslint/array-type': [
35+
'error',
36+
{ default: 'array-simple' }
37+
],
38+
'@typescript-eslint/default-param-last': 'error',
39+
'@typescript-eslint/explicit-member-accessibility': 'error',
40+
'@typescript-eslint/explicit-module-boundary-types': 'error',
41+
'@stylistic/ts/lines-between-class-members': [
42+
'error',
43+
'always',
44+
{ exceptAfterSingleLine: true }
45+
],
46+
'@typescript-eslint/naming-convention': [
47+
'error',
48+
{
49+
selector: 'default',
50+
format: ['camelCase', 'PascalCase', 'UPPER_CASE']
51+
},
52+
{ selector: 'property', format: null },
53+
{ selector: 'typeLike', format: ['PascalCase'] },
54+
{
55+
selector: 'typeParameter',
56+
format: ['PascalCase'],
57+
custom: {
58+
match: true,
59+
regex: /^T([A-Z][a-zA-Z]+)$|^[A-Z]$/u.source
60+
}
61+
},
62+
{ selector: 'enumMember', format: ['PascalCase', 'UPPER_CASE'] },
63+
{
64+
selector: 'interface',
65+
format: ['PascalCase'], // disallow "I" prefixing, but allow names like "IAM"
66+
custom: { match: false, regex: /^I[A-Z][a-z]/u.source }
67+
},
68+
{
69+
selector: 'parameter',
70+
format: ['camelCase'],
71+
leadingUnderscore: 'allow'
72+
},
73+
{
74+
selector: 'memberLike',
75+
modifiers: ['private'],
76+
format: ['PascalCase', 'camelCase'],
77+
leadingUnderscore: 'require'
78+
},
79+
{
80+
selector: 'memberLike',
81+
modifiers: ['protected'],
82+
format: ['PascalCase', 'camelCase'],
83+
leadingUnderscore: 'require'
84+
},
85+
{
86+
selector: 'memberLike',
87+
modifiers: ['public'],
88+
format: ['PascalCase', 'camelCase'],
89+
leadingUnderscore: 'forbid'
90+
}
91+
],
92+
'@typescript-eslint/no-confusing-void-expression': [
93+
'error',
94+
{ ignoreArrowShorthand: true }
95+
],
96+
'@typescript-eslint/no-dupe-class-members': 'error',
97+
'@typescript-eslint/no-dynamic-delete': 'error',
98+
'@typescript-eslint/no-extraneous-class': 'error',
99+
'@typescript-eslint/no-invalid-this': 'error',
100+
'@typescript-eslint/no-loop-func': 'error',
101+
'@typescript-eslint/no-meaningless-void-operator': 'error',
102+
'@typescript-eslint/no-mixed-enums': 'error',
103+
'@typescript-eslint/no-non-null-assertion': 'error',
104+
'@typescript-eslint/no-namespace': [
105+
'off', // todo: need to audit existing codebase to see if declare is fine
106+
{
107+
allowDeclarations: true,
108+
allowDefinitionFiles: true
56109
}
57-
},
58-
{ selector: 'enumMember', format: ['PascalCase', 'UPPER_CASE'] },
59-
{
60-
selector: 'interface',
61-
format: ['PascalCase'], // disallow "I" prefixing, but allow names like "IAM"
62-
custom: { match: false, regex: /^I[A-Z][a-z]/u.source }
63-
},
64-
{
65-
selector: 'parameter',
66-
format: ['camelCase'],
67-
leadingUnderscore: 'allow'
68-
},
69-
{
70-
selector: 'memberLike',
71-
modifiers: ['private'],
72-
format: ['PascalCase', 'camelCase'],
73-
leadingUnderscore: 'require'
74-
},
75-
{
76-
selector: 'memberLike',
77-
modifiers: ['protected'],
78-
format: ['PascalCase', 'camelCase'],
79-
leadingUnderscore: 'require'
80-
},
81-
{
82-
selector: 'memberLike',
83-
modifiers: ['public'],
84-
format: ['PascalCase', 'camelCase'],
85-
leadingUnderscore: 'forbid'
86-
}
87-
],
88-
'@typescript-eslint/no-confusing-void-expression': [
89-
'error',
90-
{ ignoreArrowShorthand: true }
91-
],
92-
'@typescript-eslint/no-dupe-class-members': 'error',
93-
'@typescript-eslint/no-dynamic-delete': 'error',
94-
'@typescript-eslint/no-extraneous-class': 'error',
95-
'@typescript-eslint/no-invalid-this': 'error',
96-
'@typescript-eslint/no-loop-func': 'error',
97-
'@typescript-eslint/no-meaningless-void-operator': 'error',
98-
'@typescript-eslint/no-mixed-enums': 'error',
99-
'@typescript-eslint/no-non-null-assertion': 'error',
100-
'@typescript-eslint/no-namespace': [
101-
'off', // todo: need to audit existing codebase to see if declare is fine
102-
{
103-
allowDeclarations: true,
104-
allowDefinitionFiles: true
105-
}
106-
],
107-
'@typescript-eslint/no-redeclare': 'error',
108-
'@typescript-eslint/no-require-imports': 'error',
109-
'@typescript-eslint/no-shadow': 'warn',
110-
'@typescript-eslint/no-this-alias': [
111-
'error',
112-
{ allowDestructuring: true }
113-
],
114-
'@typescript-eslint/no-throw-literal': 'error',
115-
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error',
116-
'@typescript-eslint/no-unnecessary-condition': 'error',
117-
'@typescript-eslint/no-unnecessary-qualifier': 'error',
118-
'@typescript-eslint/no-unnecessary-type-arguments': 'error',
119-
'@typescript-eslint/no-unused-expressions': 'error',
120-
'@typescript-eslint/no-unused-vars': [
121-
'error',
122-
{ argsIgnorePattern: '^_' }
123-
],
124-
'@typescript-eslint/no-use-before-define': [
125-
'error', // Purely stylistic b/c of TS
126-
{ typedefs: false, variables: false }
127-
],
128-
'@typescript-eslint/no-useless-constructor': 'error',
129-
'@typescript-eslint/parameter-properties': 'error',
130-
'@typescript-eslint/prefer-includes': 'error',
131-
'@typescript-eslint/prefer-readonly': 'warn',
132-
'@typescript-eslint/prefer-reduce-type-parameter': 'error',
133-
'@typescript-eslint/prefer-string-starts-ends-with': 'warn',
134-
'@typescript-eslint/prefer-ts-expect-error': 'error',
135-
'@typescript-eslint/promise-function-async': 'error',
136-
'@typescript-eslint/require-array-sort-compare': 'warn',
137-
'@typescript-eslint/sort-type-constituents': 'error',
138-
'@typescript-eslint/switch-exhaustiveness-check': 'error',
139-
'@typescript-eslint/unified-signatures': 'warn', // can be a bit wrong
140-
'array-callback-return': 'off',
141-
'block-scoped-var': 'off',
142-
'camelcase': 'off',
143-
'consistent-return': 'off', // via --noImplicitReturns
144-
'default-param-last': 'off',
145-
'dot-notation': 'off', // @typescript-eslint
146-
'guard-for-in': 'off',
147-
'init-declarations': 'off', // handled by TS & --noImplicitAny
148-
'lines-between-class-members': 'off',
149-
'@stylistic/js/lines-between-class-members': 'off',
150-
'no-dupe-class-members': 'off', // @typescript-eslint
151-
'no-import-assign': 'off',
152-
'no-invalid-this': 'off', // @typescript-eslint
153-
'no-iterator': 'off',
154-
'no-loop-func': 'off', // @typescript-eslint
155-
'no-proto': 'off', // TS2339
156-
'no-setter-return': 'off', // TS2408
157-
'no-shadow': 'off', // @typescript-eslint
158-
'no-throw-literal': 'off', // @typescript-eslint
159-
'no-underscore-dangle': 'off',
160-
'no-unused-expressions': 'off',
161-
'no-use-before-define': 'off',
162-
'no-useless-constructor': 'off', // @typescript-eslint
163-
'strict': 'off' // via --alwaysStrict
110+
],
111+
'@typescript-eslint/no-redeclare': 'error',
112+
'@typescript-eslint/no-require-imports': 'error',
113+
'@typescript-eslint/no-shadow': 'warn',
114+
'@typescript-eslint/no-this-alias': [
115+
'error',
116+
{ allowDestructuring: true }
117+
],
118+
'@typescript-eslint/no-throw-literal': 'error',
119+
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error',
120+
'@typescript-eslint/no-unnecessary-condition': 'error',
121+
'@typescript-eslint/no-unnecessary-qualifier': 'error',
122+
'@typescript-eslint/no-unnecessary-type-arguments': 'error',
123+
'@typescript-eslint/no-unused-expressions': 'error',
124+
'@typescript-eslint/no-unused-vars': [
125+
'error',
126+
{ argsIgnorePattern: '^_' }
127+
],
128+
'@typescript-eslint/no-use-before-define': [
129+
'error', // Purely stylistic b/c of TS
130+
{ typedefs: false, variables: false }
131+
],
132+
'@typescript-eslint/no-useless-constructor': 'error',
133+
'@typescript-eslint/parameter-properties': 'error',
134+
'@typescript-eslint/prefer-includes': 'error',
135+
'@typescript-eslint/prefer-readonly': 'warn',
136+
'@typescript-eslint/prefer-reduce-type-parameter': 'error',
137+
'@typescript-eslint/prefer-string-starts-ends-with': 'warn',
138+
'@typescript-eslint/prefer-ts-expect-error': 'error',
139+
'@typescript-eslint/promise-function-async': 'error',
140+
'@typescript-eslint/require-array-sort-compare': 'warn',
141+
'@typescript-eslint/sort-type-constituents': 'error',
142+
'@typescript-eslint/switch-exhaustiveness-check': 'error',
143+
'@typescript-eslint/unified-signatures': 'warn', // can be a bit wrong
144+
'array-callback-return': 'off',
145+
'block-scoped-var': 'off',
146+
'camelcase': 'off',
147+
'consistent-return': 'off', // via --noImplicitReturns
148+
'default-param-last': 'off',
149+
'dot-notation': 'off', // @typescript-eslint
150+
'guard-for-in': 'off',
151+
'init-declarations': 'off', // handled by TS & --noImplicitAny
152+
'lines-between-class-members': 'off',
153+
'@stylistic/js/lines-between-class-members': 'off',
154+
'no-dupe-class-members': 'off', // @typescript-eslint
155+
'no-import-assign': 'off',
156+
'no-invalid-this': 'off', // @typescript-eslint
157+
'no-iterator': 'off',
158+
'no-loop-func': 'off', // @typescript-eslint
159+
'no-proto': 'off', // TS2339
160+
'no-setter-return': 'off', // TS2408
161+
'no-shadow': 'off', // @typescript-eslint
162+
'no-throw-literal': 'off', // @typescript-eslint
163+
'no-underscore-dangle': 'off',
164+
'no-unused-expressions': 'off',
165+
'no-use-before-define': 'off',
166+
'no-useless-constructor': 'off', // @typescript-eslint
167+
'strict': 'off' // via --alwaysStrict
168+
}
164169
}
165-
};
170+
];
166171

167172
return config;
168173
}

README.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@ const configAckamaBase = require('eslint-config-ackama');
1616
/** @type {import('eslint').Linter.FlatConfig[]} */
1717
const config = [
1818
{ files: ['**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts}'] },
19-
/** @type {import('eslint').Linter.FlatConfig} */ (configAckamaBase)
19+
.../** @type {import('eslint').Linter.FlatConfig[]} */ (configAckamaBase)
2020
];
2121

2222
module.exports = config;
2323
```
2424

2525
By default, the configurations use the
2626
["flat config"](https://eslint.org/blog/2022/08/new-config-system-part-2/)
27-
system introduced in ESLint 8.x. This allows for a more flexible and powerful
28-
configuration system, but it does require a bit more boilerplate to set up.
27+
system introduced in ESLint v8.23.0, with each configuration exporting an array
28+
of one or more configuration objects. This allows for a more flexible and
29+
powerful configuration system, but it does require a bit more boilerplate to set
30+
up.
2931

3032
You can set the `ESLINT_USE_FLAT_CONFIG` environment variable to `false` if you
3133
wish to have the configurations use the legacy format for use with
@@ -57,7 +59,7 @@ const globals = require('globals');
5759
/** @type {import('eslint').Linter.FlatConfig[]} */
5860
const config = [
5961
{ files: ['**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts}'] },
60-
/** @type {import('eslint').Linter.FlatConfig} */ (configAckamaBase),
62+
.../** @type {import('eslint').Linter.FlatConfig[]} */ (configAckamaBase),
6163
{
6264
languageOptions: {
6365
globals: {
@@ -179,7 +181,7 @@ const configAckamaBase = require('eslint-config-ackama');
179181
const config = [
180182
{ files: ['**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts}'] },
181183
{ ignores: ['infra'] },
182-
/** @type {import('eslint').Linter.FlatConfig} */ (configAckamaBase)
184+
.../** @type {import('eslint').Linter.FlatConfig[]} */ (configAckamaBase)
183185
];
184186

185187
module.exports = config;
@@ -219,17 +221,19 @@ const globals = require('globals');
219221
const config = [
220222
{ files: ['**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts}'] },
221223
{ ignores: ['infra'] },
222-
/** @type {import('eslint').Linter.FlatConfig} */ (configAckamaBase),
223-
/** @type {import('eslint').Linter.FlatConfig} */ (configAckamaTypeScript),
224+
.../** @type {import('eslint').Linter.FlatConfig[]} */ (configAckamaBase),
225+
.../** @type {import('eslint').Linter.FlatConfig[]} */ (
226+
configAckamaTypeScript
227+
),
224228
{
225229
languageOptions: {
226230
parserOptions: { project: true },
227231
globals: globals.commonjs
228232
}
229233
},
230-
/** @type {import('eslint').Linter.FlatConfig} */ (configAckamaReact),
234+
.../** @type {import('eslint').Linter.FlatConfig[]} */ (configAckamaReact),
231235
...[
232-
/** @type {import('eslint').Linter.FlatConfig} */ (configAckamaJest),
236+
.../** @type {import('eslint').Linter.FlatConfig[]} */ (configAckamaJest),
233237
/** @type {import('eslint').Linter.FlatConfig} */ ({
234238
rules: { 'jest/prefer-expect-assertions': 'off' }
235239
})

0 commit comments

Comments
 (0)