@prairielearn/eslint-config 0.0.1 → 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.
Files changed (71) hide show
  1. package/.turbo/turbo-build.log +0 -0
  2. package/CHANGELOG.md +13 -0
  3. package/README.md +111 -43
  4. package/dist/configs/base.d.ts +7 -0
  5. package/dist/configs/base.d.ts.map +1 -0
  6. package/dist/configs/base.js +121 -0
  7. package/dist/configs/base.js.map +1 -0
  8. package/dist/configs/imports.d.ts +6 -0
  9. package/dist/configs/imports.d.ts.map +1 -0
  10. package/dist/configs/imports.js +44 -0
  11. package/dist/configs/imports.js.map +1 -0
  12. package/dist/configs/jsdoc.d.ts +6 -0
  13. package/dist/configs/jsdoc.d.ts.map +1 -0
  14. package/dist/configs/jsdoc.js +70 -0
  15. package/dist/configs/jsdoc.js.map +1 -0
  16. package/dist/configs/lodash.d.ts +6 -0
  17. package/dist/configs/lodash.d.ts.map +1 -0
  18. package/dist/configs/lodash.js +24 -0
  19. package/dist/configs/lodash.js.map +1 -0
  20. package/dist/configs/perfectionist.d.ts +8 -0
  21. package/dist/configs/perfectionist.d.ts.map +1 -0
  22. package/dist/configs/perfectionist.js +43 -0
  23. package/dist/configs/perfectionist.js.map +1 -0
  24. package/dist/configs/prairielearn.d.ts +13 -0
  25. package/dist/configs/prairielearn.d.ts.map +1 -0
  26. package/dist/configs/prairielearn.js +29 -0
  27. package/dist/configs/prairielearn.js.map +1 -0
  28. package/dist/configs/react.d.ts +6 -0
  29. package/dist/configs/react.d.ts.map +1 -0
  30. package/dist/configs/react.js +63 -0
  31. package/dist/configs/react.js.map +1 -0
  32. package/dist/configs/stylistic.d.ts +6 -0
  33. package/dist/configs/stylistic.d.ts.map +1 -0
  34. package/dist/configs/stylistic.js +51 -0
  35. package/dist/configs/stylistic.js.map +1 -0
  36. package/dist/configs/tanstack.d.ts +6 -0
  37. package/dist/configs/tanstack.d.ts.map +1 -0
  38. package/dist/configs/tanstack.js +19 -0
  39. package/dist/configs/tanstack.js.map +1 -0
  40. package/dist/configs/typescript.d.ts +11 -0
  41. package/dist/configs/typescript.d.ts.map +1 -0
  42. package/dist/configs/typescript.js +106 -0
  43. package/dist/configs/typescript.js.map +1 -0
  44. package/dist/configs/unicorn.d.ts +6 -0
  45. package/dist/configs/unicorn.d.ts.map +1 -0
  46. package/dist/configs/unicorn.js +74 -0
  47. package/dist/configs/unicorn.js.map +1 -0
  48. package/dist/configs/vitest.d.ts +6 -0
  49. package/dist/configs/vitest.d.ts.map +1 -0
  50. package/dist/configs/vitest.js +26 -0
  51. package/dist/configs/vitest.js.map +1 -0
  52. package/dist/index.d.ts +76 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +112 -0
  55. package/dist/index.js.map +1 -0
  56. package/package.json +49 -7
  57. package/src/configs/base.ts +127 -0
  58. package/src/configs/imports.ts +50 -0
  59. package/src/configs/jsdoc.ts +72 -0
  60. package/src/configs/lodash.ts +26 -0
  61. package/src/configs/perfectionist.ts +48 -0
  62. package/src/configs/prairielearn.ts +42 -0
  63. package/src/configs/react.ts +82 -0
  64. package/src/configs/stylistic.ts +54 -0
  65. package/src/configs/tanstack.ts +21 -0
  66. package/src/configs/typescript.ts +108 -0
  67. package/src/configs/unicorn.ts +84 -0
  68. package/src/configs/vitest.ts +31 -0
  69. package/src/index.ts +190 -0
  70. package/src/types.d.ts +6 -0
  71. package/tsconfig.json +7 -0
@@ -0,0 +1,82 @@
1
+ import eslintReact from '@eslint-react/eslint-plugin';
2
+ import type { TSESLint } from '@typescript-eslint/utils';
3
+ import jsxA11yX from 'eslint-plugin-jsx-a11y-x';
4
+ import reactHooks from 'eslint-plugin-react-hooks';
5
+ import reactYouMightNotNeedAnEffect from 'eslint-plugin-react-you-might-not-need-an-effect';
6
+
7
+ // The eslint-react config has plugins/settings/rules but the type doesn't expose them all
8
+ const eslintReactConfig = eslintReact.configs['recommended-typescript'] as {
9
+ plugins?: TSESLint.FlatConfig.Plugins;
10
+ rules?: TSESLint.FlatConfig.Rules;
11
+ settings?: TSESLint.FlatConfig.Settings;
12
+ };
13
+
14
+ /**
15
+ * React, React hooks, accessibility, and related rules.
16
+ */
17
+ export function reactConfig(): TSESLint.FlatConfig.ConfigArray {
18
+ return [
19
+ {
20
+ plugins: {
21
+ 'jsx-a11y-x': jsxA11yX,
22
+ 'react-hooks': reactHooks,
23
+ 'react-you-might-not-need-an-effect': reactYouMightNotNeedAnEffect,
24
+ ...eslintReactConfig.plugins,
25
+ },
26
+
27
+ rules: {
28
+ // React hooks
29
+ 'react-hooks/exhaustive-deps': 'error',
30
+ 'react-hooks/rules-of-hooks': 'error',
31
+
32
+ // react-you-might-not-need-an-effect rules as errors
33
+ ...Object.fromEntries(
34
+ Object.keys(reactYouMightNotNeedAnEffect.configs?.recommended?.rules ?? {}).map(
35
+ (ruleName: string) => [ruleName, 'error'],
36
+ ),
37
+ ),
38
+
39
+ // eslint-react recommended rules as errors
40
+ ...Object.fromEntries(
41
+ Object.entries(eslintReactConfig.rules ?? {}).map(
42
+ ([ruleName, severity]: [string, unknown]) => [
43
+ ruleName,
44
+ severity === 'off' ? 'off' : 'error',
45
+ ],
46
+ ),
47
+ ),
48
+ // We want to be able to use `useState` without the setter function for
49
+ // https://tkdodo.eu/blog/react-query-fa-qs#2-the-queryclient-is-not-stable
50
+ '@eslint-react/naming-convention/use-state': 'off',
51
+ // Forbid `snake_case` props.
52
+ '@eslint-react/no-forbidden-props': ['error', { forbid: ['/_/'] }],
53
+
54
+ // jsx-a11y strict rules
55
+ ...jsxA11yX.configs.strict.rules,
56
+ 'jsx-a11y-x/anchor-ambiguous-text': 'error',
57
+ 'jsx-a11y-x/lang': 'error',
58
+ 'jsx-a11y-x/no-aria-hidden-on-focusable': 'error',
59
+ // Bootstrap turns some elements into interactive elements.
60
+ 'jsx-a11y-x/no-noninteractive-element-to-interactive-role': [
61
+ 'error',
62
+ {
63
+ li: ['menuitem', 'option', 'row', 'tab', 'treeitem'],
64
+ ol: ['listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid'],
65
+ table: ['grid'],
66
+ td: ['gridcell'],
67
+ ul: ['listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid', 'role'],
68
+ },
69
+ ],
70
+ },
71
+
72
+ settings: {
73
+ 'jsx-a11y-x': {
74
+ attributes: {
75
+ for: ['htmlFor'],
76
+ },
77
+ },
78
+ ...eslintReactConfig.settings,
79
+ },
80
+ },
81
+ ];
82
+ }
@@ -0,0 +1,54 @@
1
+ import stylistic from '@stylistic/eslint-plugin';
2
+ import type { TSESLint } from '@typescript-eslint/utils';
3
+
4
+ /**
5
+ * Stylistic formatting rules.
6
+ */
7
+ export function stylisticConfig(): TSESLint.FlatConfig.ConfigArray {
8
+ return [
9
+ {
10
+ plugins: {
11
+ '@stylistic': stylistic,
12
+ },
13
+
14
+ rules: {
15
+ '@stylistic/jsx-curly-brace-presence': [
16
+ 'error',
17
+ { children: 'never', propElementValues: 'always', props: 'never' },
18
+ ],
19
+
20
+ '@stylistic/jsx-self-closing-comp': [
21
+ 'error',
22
+ {
23
+ component: true,
24
+ html: true,
25
+ },
26
+ ],
27
+ '@stylistic/jsx-tag-spacing': [
28
+ 'error',
29
+ {
30
+ afterOpening: 'never',
31
+ beforeClosing: 'allow',
32
+ beforeSelfClosing: 'always',
33
+ closingSlash: 'never',
34
+ },
35
+ ],
36
+ '@stylistic/lines-between-class-members': [
37
+ 'error',
38
+ 'always',
39
+ { exceptAfterSingleLine: true },
40
+ ],
41
+ '@stylistic/no-tabs': 'error',
42
+ '@stylistic/padding-line-between-statements': [
43
+ 'error',
44
+ { blankLine: 'always', next: 'function', prev: '*' },
45
+ { blankLine: 'always', next: '*', prev: 'import' },
46
+ { blankLine: 'any', next: 'import', prev: 'import' },
47
+ ],
48
+ // Blocks double-quote strings (unless a single quote is present in the
49
+ // string) and backticks (unless there is a tag or substitution in place).
50
+ '@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
51
+ },
52
+ },
53
+ ];
54
+ }
@@ -0,0 +1,21 @@
1
+ import pluginQuery from '@tanstack/eslint-plugin-query';
2
+ import type { TSESLint } from '@typescript-eslint/utils';
3
+
4
+ /**
5
+ * TanStack Query (React Query) rules.
6
+ */
7
+ export function tanstackConfig(): TSESLint.FlatConfig.ConfigArray {
8
+ return [
9
+ {
10
+ plugins: {
11
+ '@tanstack/query': pluginQuery,
12
+ },
13
+
14
+ rules: {
15
+ // https://github.com/TanStack/query/blob/6402d756b702ac560b69a5ce84d6e4e764b96451/packages/eslint-plugin-query/src/index.ts#L43
16
+ ...pluginQuery.configs['flat/recommended'][0].rules,
17
+ '@tanstack/query/no-rest-destructuring': 'error',
18
+ },
19
+ },
20
+ ];
21
+ }
@@ -0,0 +1,108 @@
1
+ import type { TSESLint } from '@typescript-eslint/utils';
2
+
3
+ /**
4
+ * TypeScript-specific rules (non-type-aware).
5
+ */
6
+ export function typescriptConfig(): TSESLint.FlatConfig.ConfigArray {
7
+ return [
8
+ {
9
+ rules: {
10
+ '@typescript-eslint/consistent-type-imports': [
11
+ 'error',
12
+ { fixStyle: 'inline-type-imports' },
13
+ ],
14
+ // We use empty functions in quite a few places, so we'll disable this rule.
15
+ '@typescript-eslint/no-empty-function': 'off',
16
+ // Look, sometimes we just want to use `any`.
17
+ '@typescript-eslint/no-explicit-any': 'off',
18
+ // This was enabled when we upgraded to `@typescript-eslint/*` v6.
19
+ '@typescript-eslint/no-dynamic-delete': 'off',
20
+ // We use `!` to assert that a value is not `null` or `undefined`.
21
+ '@typescript-eslint/no-non-null-assertion': 'off',
22
+ // Replaces the standard `no-unused-vars` rule.
23
+ '@typescript-eslint/no-unused-vars': [
24
+ 'error',
25
+ {
26
+ args: 'after-used',
27
+ argsIgnorePattern: '^_', // Args can be _
28
+ varsIgnorePattern: '^_.', // This includes lodash, which should be considered
29
+ },
30
+ ],
31
+ },
32
+ },
33
+ ];
34
+ }
35
+
36
+ /**
37
+ * Type-aware TypeScript rules.
38
+ * These require a tsconfig.json to be configured.
39
+ */
40
+ export function typescriptTypeAwareRules(): TSESLint.FlatConfig.Rules {
41
+ return {
42
+ '@typescript-eslint/no-base-to-string': 'off',
43
+ '@typescript-eslint/no-invalid-void-type': [
44
+ 'error',
45
+ {
46
+ allowAsThisParameter: true,
47
+ },
48
+ ],
49
+ '@typescript-eslint/no-unsafe-argument': 'off',
50
+ '@typescript-eslint/no-unsafe-assignment': 'off',
51
+ '@typescript-eslint/no-unsafe-call': 'off',
52
+ '@typescript-eslint/no-unsafe-member-access': 'off',
53
+ '@typescript-eslint/no-unsafe-return': 'off',
54
+ // Some functions are required to be async, but don't actually use any async code.
55
+ '@typescript-eslint/require-await': 'off',
56
+ // We don't always check that we got a error when a promise is rejected.
57
+ '@typescript-eslint/no-misused-promises': [
58
+ 'error',
59
+ {
60
+ checksConditionals: true,
61
+ checksSpreads: true,
62
+ checksVoidReturn: {
63
+ // Common usage with `async` functions
64
+ arguments: false,
65
+ // Common usage with `async` onClick handlers
66
+ attributes: false,
67
+ inheritedMethods: true,
68
+ // Common usage with e.g. setState
69
+ properties: false,
70
+ returns: true,
71
+ variables: true,
72
+ },
73
+ },
74
+ ],
75
+ '@typescript-eslint/no-unnecessary-condition': [
76
+ 'error',
77
+ { allowConstantLoopConditions: 'only-allowed-literals' },
78
+ ],
79
+ '@typescript-eslint/only-throw-error': [
80
+ 'error',
81
+ {
82
+ allow: [
83
+ {
84
+ from: 'file',
85
+ name: 'HttpRedirect',
86
+ },
87
+ ],
88
+ allowRethrowing: true,
89
+ allowThrowingAny: true,
90
+ allowThrowingUnknown: true,
91
+ },
92
+ ],
93
+ '@typescript-eslint/prefer-nullish-coalescing': 'off',
94
+ '@typescript-eslint/prefer-promise-reject-errors': 'off',
95
+ '@typescript-eslint/prefer-regexp-exec': 'off',
96
+ '@typescript-eslint/restrict-template-expressions': [
97
+ 'error',
98
+ {
99
+ allow: [{ from: 'lib', name: ['Error', 'URL', 'URLSearchParams'] }],
100
+ allowAny: true,
101
+ allowBoolean: true,
102
+ allowNullish: true,
103
+ allowNumber: true,
104
+ allowRegExp: true,
105
+ },
106
+ ],
107
+ };
108
+ }
@@ -0,0 +1,84 @@
1
+ import type { TSESLint } from '@typescript-eslint/utils';
2
+ import eslintPluginUnicorn from 'eslint-plugin-unicorn';
3
+
4
+ /**
5
+ * Unicorn plugin rules with PrairieLearn-specific overrides.
6
+ */
7
+ export function unicornConfig(): TSESLint.FlatConfig.ConfigArray {
8
+ return [
9
+ {
10
+ plugins: {
11
+ unicorn: eslintPluginUnicorn,
12
+ },
13
+
14
+ rules: {
15
+ ...eslintPluginUnicorn.configs.recommended.rules,
16
+
17
+ // These rules don't align with our own style guidelines
18
+ 'unicorn/filename-case': 'off', // We don't enforce specific styles for filenames
19
+ 'unicorn/no-anonymous-default-export': 'off', // We use this for all of our pages
20
+ 'unicorn/no-array-callback-reference': 'off',
21
+ 'unicorn/no-array-method-this-argument': 'off',
22
+ 'unicorn/no-array-reduce': 'off', // Sometimes, an array reduce is more readable
23
+ 'unicorn/no-array-reverse': 'off', // `Array.prototype.toReversed` is not yet supported by our TypeScript config
24
+ 'unicorn/no-array-sort': 'off', // Disabling for the time being to avoid unnecessary diffs
25
+ 'unicorn/no-hex-escape': 'off',
26
+ 'unicorn/no-lonely-if': 'off', // https://github.com/PrairieLearn/PrairieLearn/pull/12546#discussion_r2252261293
27
+ 'unicorn/no-null': 'off',
28
+ 'unicorn/no-useless-undefined': 'off', // Explicit undefined is more readable than implicit undefined
29
+ 'unicorn/prefer-code-point': 'off',
30
+ 'unicorn/prefer-dom-node-dataset': 'off', // https://github.com/PrairieLearn/PrairieLearn/pull/12546#discussion_r2261095992
31
+ 'unicorn/prefer-export-from': 'off', // https://github.com/PrairieLearn/PrairieLearn/pull/12546#discussion_r2252265000
32
+ 'unicorn/prefer-string-raw': 'off', // We don't use `String.raw` in our codebase
33
+ 'unicorn/prefer-ternary': 'off', // if/else can be more readable than a ternary
34
+ 'unicorn/prefer-top-level-await': 'off', // we use this on a lot of pages
35
+ 'unicorn/prefer-type-error': 'off',
36
+ 'unicorn/prevent-abbreviations': 'off',
37
+
38
+ // These rules have many violations. Decisions about enabling the rules have been deferred.
39
+ 'unicorn/catch-error-name': 'off', // 200+ violations
40
+ 'unicorn/no-array-for-each': 'off', // 300+ violations
41
+ 'unicorn/no-await-expression-member': 'off', // 400+ violations
42
+ 'unicorn/no-negated-condition': 'off', // 150+ violations
43
+ 'unicorn/prefer-global-this': 'off', // 150+ violations
44
+ 'unicorn/prefer-node-protocol': 'off', // 100+ violations
45
+ 'unicorn/switch-case-braces': 'off', // 200+ violations
46
+
47
+ // TODO: investigate, < 100 violations
48
+ 'unicorn/consistent-assert': 'off',
49
+ 'unicorn/consistent-function-scoping': 'off',
50
+ 'unicorn/escape-case': 'off',
51
+ 'unicorn/import-style': 'off',
52
+ 'unicorn/numeric-separators-style': 'off',
53
+ 'unicorn/prefer-query-selector': 'off',
54
+ 'unicorn/prefer-spread': 'off',
55
+ 'unicorn/prefer-switch': 'off',
56
+ 'unicorn/text-encoding-identifier-case': 'off',
57
+
58
+ // TODO: investigated and manual fixes are required
59
+ 'unicorn/no-object-as-default-parameter': 'off',
60
+ 'unicorn/prefer-add-event-listener': 'off',
61
+ 'unicorn/prefer-dom-node-text-content': 'off',
62
+ 'unicorn/prefer-event-target': 'off',
63
+
64
+ // False positives
65
+ 'unicorn/error-message': 'off',
66
+ 'unicorn/prefer-at': 'off', // https://github.com/microsoft/TypeScript/issues/47660#issuecomment-3146907649
67
+ 'unicorn/throw-new-error': 'off',
68
+
69
+ // Duplicated from other lint rules
70
+ 'unicorn/no-static-only-class': 'off',
71
+ 'unicorn/no-this-assignment': 'off',
72
+ 'unicorn/prefer-module': 'off',
73
+
74
+ // https://github.com/PrairieLearn/PrairieLearn/pull/12545/files#r2252069292
75
+ 'unicorn/no-for-loop': 'off',
76
+
77
+ // Conflicts with prettier
78
+ 'unicorn/no-nested-ternary': 'off',
79
+ 'unicorn/number-literal-case': 'off',
80
+ 'unicorn/template-indent': 'off',
81
+ },
82
+ },
83
+ ];
84
+ }
@@ -0,0 +1,31 @@
1
+ import type { TSESLint } from '@typescript-eslint/utils';
2
+ import vitest from '@vitest/eslint-plugin';
3
+
4
+ /**
5
+ * Vitest test framework rules.
6
+ */
7
+ export function vitestConfig(): TSESLint.FlatConfig.ConfigArray {
8
+ return [
9
+ {
10
+ plugins: {
11
+ vitest,
12
+ },
13
+
14
+ rules: {
15
+ // Use the recommended rules for vitest
16
+ ...vitest.configs.recommended.rules,
17
+
18
+ // We are disabling the test for a reason.
19
+ 'vitest/no-disabled-tests': 'off',
20
+
21
+ // This gives a lot of false positives; we sometimes author tests that
22
+ // have the assertion in a helper function. We could refactor them in
23
+ // the future, but for now we'll disable this rule.
24
+ 'vitest/expect-expect': 'off',
25
+
26
+ // We violate this rule in a lot of places. We'll turn it off for now.
27
+ 'vitest/no-identical-title': 'off',
28
+ },
29
+ },
30
+ ];
31
+ }
package/src/index.ts ADDED
@@ -0,0 +1,190 @@
1
+ import type { TSESLint } from '@typescript-eslint/utils';
2
+ import { globalIgnores } from 'eslint/config';
3
+ import tseslint from 'typescript-eslint';
4
+
5
+ import { baseConfig } from './configs/base.js';
6
+ import { importsConfig } from './configs/imports.js';
7
+ import { jsdocConfig } from './configs/jsdoc.js';
8
+ import { lodashConfig } from './configs/lodash.js';
9
+ import { perfectionistConfig } from './configs/perfectionist.js';
10
+ import { type PrairieLearnPluginOptions, prairieLearnConfig } from './configs/prairielearn.js';
11
+ import { reactConfig } from './configs/react.js';
12
+ import { stylisticConfig } from './configs/stylistic.js';
13
+ import { tanstackConfig } from './configs/tanstack.js';
14
+ import { typescriptConfig, typescriptTypeAwareRules } from './configs/typescript.js';
15
+ import { unicornConfig } from './configs/unicorn.js';
16
+ import { vitestConfig } from './configs/vitest.js';
17
+
18
+ export interface PrairieLearnEslintConfigOptions {
19
+ /**
20
+ * Root directory for TypeScript project service.
21
+ * Required for type-aware linting.
22
+ */
23
+ tsconfigRootDir: string;
24
+
25
+ /**
26
+ * Glob patterns for files to apply type-aware rules to.
27
+ * @default ['**\/*.{ts,tsx}']
28
+ */
29
+ typeAwareFiles?: string[];
30
+
31
+ /**
32
+ * Files to allow in defaultProject for type-aware linting.
33
+ * Useful for config files outside the main tsconfig.
34
+ * @default ['*.config.ts', '*.config.mts', 'vitest.config.ts']
35
+ */
36
+ allowDefaultProject?: string[];
37
+
38
+ /**
39
+ * Enable `@prairielearn/eslint-plugin` rules.
40
+ * @default true
41
+ */
42
+ prairielearn?: boolean;
43
+
44
+ /**
45
+ * Options for the `@prairielearn/eslint-plugin` rules.
46
+ */
47
+ prairieLearnOptions?: PrairieLearnPluginOptions;
48
+
49
+ /**
50
+ * Global ignores to apply.
51
+ * Will be merged with default ignores.
52
+ */
53
+ ignores?: string[];
54
+
55
+ /**
56
+ * Disable specific config modules.
57
+ */
58
+ disable?: {
59
+ react?: boolean;
60
+ vitest?: boolean;
61
+ perfectionist?: boolean;
62
+ unicorn?: boolean;
63
+ jsdoc?: boolean;
64
+ tanstack?: boolean;
65
+ lodash?: boolean;
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Creates a PrairieLearn ESLint configuration array.
71
+ *
72
+ * @example
73
+ * ```js
74
+ * // eslint.config.mjs
75
+ * import { prairielearn } from '@prairielearn/eslint-config';
76
+ *
77
+ * export default [
78
+ * ...prairielearn({
79
+ * tsconfigRootDir: import.meta.dirname,
80
+ * }),
81
+ * // Add your project-specific rules here
82
+ * ];
83
+ * ```
84
+ */
85
+ export function prairielearn(
86
+ options: PrairieLearnEslintConfigOptions,
87
+ ): TSESLint.FlatConfig.ConfigArray {
88
+ const {
89
+ tsconfigRootDir,
90
+ typeAwareFiles = ['**/*.{ts,tsx}'],
91
+ allowDefaultProject = ['*.config.ts', '*.config.mts', 'vitest.config.ts'],
92
+ prairielearn: enablePrairielearn = true,
93
+ prairieLearnOptions,
94
+ ignores = [],
95
+ disable = {},
96
+ } = options;
97
+
98
+ const jsFiles = ['**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}'];
99
+
100
+ const configs: TSESLint.FlatConfig.ConfigArray = [
101
+ // Base typescript-eslint configs (scoped to JS/TS files)
102
+ ...tseslint.config({
103
+ extends: [...tseslint.configs.stylistic, ...tseslint.configs.strict],
104
+ files: jsFiles,
105
+ }),
106
+ // Base configs (always included)
107
+ ...baseConfig(),
108
+ // TypeScript config (scoped to JS/TS files)
109
+ {
110
+ files: jsFiles,
111
+ ...typescriptConfig()[0],
112
+ },
113
+ ...importsConfig(),
114
+ ...stylisticConfig(),
115
+ ];
116
+
117
+ // Optional configs
118
+ if (!disable.react) {
119
+ configs.push(...reactConfig());
120
+ }
121
+
122
+ if (!disable.vitest) {
123
+ configs.push(...vitestConfig());
124
+ }
125
+
126
+ if (!disable.perfectionist) {
127
+ configs.push(...perfectionistConfig());
128
+ }
129
+
130
+ if (!disable.unicorn) {
131
+ configs.push(...unicornConfig());
132
+ }
133
+
134
+ if (!disable.jsdoc) {
135
+ configs.push(...jsdocConfig());
136
+ }
137
+
138
+ if (!disable.tanstack) {
139
+ configs.push(...tanstackConfig());
140
+ }
141
+
142
+ if (!disable.lodash) {
143
+ configs.push(...lodashConfig());
144
+ }
145
+
146
+ if (enablePrairielearn) {
147
+ configs.push(...prairieLearnConfig(prairieLearnOptions));
148
+ }
149
+
150
+ // Type-aware rules (applied to specified files)
151
+ // We use tseslint.config() to properly handle the extends syntax
152
+ configs.push(
153
+ ...tseslint.config({
154
+ extends: [
155
+ tseslint.configs.recommendedTypeCheckedOnly,
156
+ tseslint.configs.stylisticTypeCheckedOnly,
157
+ ],
158
+ files: typeAwareFiles,
159
+ languageOptions: {
160
+ parserOptions: {
161
+ projectService: {
162
+ allowDefaultProject,
163
+ },
164
+ tsconfigRootDir,
165
+ },
166
+ },
167
+ rules: typescriptTypeAwareRules(),
168
+ }),
169
+ );
170
+
171
+ // Default ignores
172
+ return [
173
+ ...configs,
174
+ globalIgnores(['.yarn/*', 'node_modules/*', 'dist/*', 'coverage/*', ...ignores]),
175
+ ];
176
+ }
177
+
178
+ // Re-export individual configs for advanced use
179
+ export { baseConfig } from './configs/base.js';
180
+ export { importsConfig } from './configs/imports.js';
181
+ export { jsdocConfig } from './configs/jsdoc.js';
182
+ export { lodashConfig } from './configs/lodash.js';
183
+ export { perfectionistConfig } from './configs/perfectionist.js';
184
+ export { prairieLearnConfig, type PrairieLearnPluginOptions } from './configs/prairielearn.js';
185
+ export { reactConfig } from './configs/react.js';
186
+ export { stylisticConfig } from './configs/stylistic.js';
187
+ export { tanstackConfig } from './configs/tanstack.js';
188
+ export { typescriptConfig, typescriptTypeAwareRules } from './configs/typescript.js';
189
+ export { unicornConfig } from './configs/unicorn.js';
190
+ export { vitestConfig } from './configs/vitest.js';
package/src/types.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ // Type declarations for ESLint plugins that don't provide their own types
2
+
3
+ declare module 'eslint-plugin-no-floating-promise';
4
+ declare module 'eslint-plugin-jsx-a11y-x';
5
+ declare module 'eslint-plugin-you-dont-need-lodash-underscore';
6
+ declare module 'eslint-plugin-react-you-might-not-need-an-effect';
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "@prairielearn/tsconfig/tsconfig.package.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ }
7
+ }