@workleap/eslint-configs 0.0.2

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 (76) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +201 -0
  3. package/README.md +16 -0
  4. package/dist/by-project-type/defineMonorepoWorkspaceConfig.d.ts +15 -0
  5. package/dist/by-project-type/defineMonorepoWorkspaceConfig.js +51 -0
  6. package/dist/by-project-type/defineMonorepoWorkspaceConfig.js.map +1 -0
  7. package/dist/by-project-type/defineReactLibraryConfig.d.ts +27 -0
  8. package/dist/by-project-type/defineReactLibraryConfig.js +96 -0
  9. package/dist/by-project-type/defineReactLibraryConfig.js.map +1 -0
  10. package/dist/by-project-type/defineTypescriptLibraryConfig.d.ts +19 -0
  11. package/dist/by-project-type/defineTypescriptLibraryConfig.js +72 -0
  12. package/dist/by-project-type/defineTypescriptLibraryConfig.js.map +1 -0
  13. package/dist/by-project-type/defineWebApplicationConfig.d.ts +27 -0
  14. package/dist/by-project-type/defineWebApplicationConfig.js +96 -0
  15. package/dist/by-project-type/defineWebApplicationConfig.js.map +1 -0
  16. package/dist/core.d.ts +7 -0
  17. package/dist/core.js +184 -0
  18. package/dist/core.js.map +1 -0
  19. package/dist/index.d.ts +15 -0
  20. package/dist/index.js +22 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/jest.d.ts +7 -0
  23. package/dist/jest.js +55 -0
  24. package/dist/jest.js.map +1 -0
  25. package/dist/jsxAlly.d.ts +7 -0
  26. package/dist/jsxAlly.js +51 -0
  27. package/dist/jsxAlly.js.map +1 -0
  28. package/dist/packageJson.d.ts +7 -0
  29. package/dist/packageJson.js +47 -0
  30. package/dist/packageJson.js.map +1 -0
  31. package/dist/plugins/strictCssModulesNames.d.ts +7 -0
  32. package/dist/plugins/strictCssModulesNames.js +70 -0
  33. package/dist/plugins/strictCssModulesNames.js.map +1 -0
  34. package/dist/plugins/workleapPlugin.d.ts +5 -0
  35. package/dist/plugins/workleapPlugin.js +15 -0
  36. package/dist/plugins/workleapPlugin.js.map +1 -0
  37. package/dist/react.d.ts +7 -0
  38. package/dist/react.js +189 -0
  39. package/dist/react.js.map +1 -0
  40. package/dist/storybook.d.ts +8 -0
  41. package/dist/storybook.js +46 -0
  42. package/dist/storybook.js.map +1 -0
  43. package/dist/testingLibrary.d.ts +8 -0
  44. package/dist/testingLibrary.js +43 -0
  45. package/dist/testingLibrary.js.map +1 -0
  46. package/dist/types.d.ts +19 -0
  47. package/dist/types.js +9 -0
  48. package/dist/types.js.map +1 -0
  49. package/dist/typescript.d.ts +7 -0
  50. package/dist/typescript.js +139 -0
  51. package/dist/typescript.js.map +1 -0
  52. package/dist/vitest.d.ts +7 -0
  53. package/dist/vitest.js +40 -0
  54. package/dist/vitest.js.map +1 -0
  55. package/dist/yaml.d.ts +7 -0
  56. package/dist/yaml.js +31 -0
  57. package/dist/yaml.js.map +1 -0
  58. package/package.json +82 -0
  59. package/src/by-project-type/defineMonorepoWorkspaceConfig.ts +45 -0
  60. package/src/by-project-type/defineReactLibraryConfig.ts +81 -0
  61. package/src/by-project-type/defineTypescriptLibraryConfig.ts +61 -0
  62. package/src/by-project-type/defineWebApplicationConfig.ts +114 -0
  63. package/src/core.ts +138 -0
  64. package/src/index.ts +16 -0
  65. package/src/jest.ts +53 -0
  66. package/src/jsxAlly.ts +52 -0
  67. package/src/packageJson.ts +48 -0
  68. package/src/plugins/strictCssModulesNames.ts +75 -0
  69. package/src/plugins/workleapPlugin.ts +7 -0
  70. package/src/react.ts +175 -0
  71. package/src/storybook.ts +53 -0
  72. package/src/testingLibrary.ts +48 -0
  73. package/src/types.ts +27 -0
  74. package/src/typescript.ts +133 -0
  75. package/src/vitest.ts +41 -0
  76. package/src/yaml.ts +32 -0
package/src/jest.ts ADDED
@@ -0,0 +1,53 @@
1
+ import type { Linter } from "eslint";
2
+ import jestPlugin from "eslint-plugin-jest";
3
+ import globals from "globals";
4
+ import type { ConfigWithExtends } from "./types.ts";
5
+
6
+ export interface JestConfigOptions {
7
+ rules?: Partial<Linter.RulesRecord>;
8
+ }
9
+
10
+ export const jestGlobalIgnores = [];
11
+
12
+ export function jestConfig(options: JestConfigOptions = {}) {
13
+ const {
14
+ rules = {}
15
+ } = options;
16
+
17
+ const config: ConfigWithExtends[] = [{
18
+ name: "@workleap/eslint-configs/jest",
19
+ files: [
20
+ "**/*.test.{js,jsx,ts,tsx}",
21
+ "**/*-test.{js,jsx,ts,tsx}",
22
+ "**/__tests__/*.{js,jsx,ts,tsx}",
23
+ "**/test.{js,jsx,ts,tsx}"
24
+ ],
25
+ extends: [
26
+ jestPlugin.configs["flat/recommended"]
27
+ ],
28
+ languageOptions: {
29
+ globals: {
30
+ ...globals.browser,
31
+ ...globals.es2024,
32
+ ...globals.node,
33
+ ...globals.commonjs,
34
+ ...globals.jest
35
+ }
36
+ },
37
+ settings: {
38
+ jest: {
39
+ version: "detect"
40
+ }
41
+ },
42
+ rules: {
43
+ // Gives better failure messages for array checks.
44
+ "jest/prefer-to-contain": "error",
45
+ // Prefer spies to allow for automatic restoration.
46
+ "jest/prefer-spy-on": "error",
47
+ // Positioned last to allow the consumer to override any rules.
48
+ ...rules
49
+ }
50
+ }];
51
+
52
+ return config;
53
+ };
package/src/jsxAlly.ts ADDED
@@ -0,0 +1,52 @@
1
+ import type { Linter } from "eslint";
2
+ import jsxA11yPlugin from "eslint-plugin-jsx-a11y";
3
+ import type { ConfigWithExtends } from "./types.ts";
4
+
5
+ export interface JsxAllyConfigOptions {
6
+ rules?: Partial<Linter.RulesRecord>;
7
+ }
8
+
9
+ export const jsxAllyGlobalIgnores = [];
10
+
11
+ export function jsxAllyConfig(options: JsxAllyConfigOptions = {}) {
12
+ const {
13
+ rules = {}
14
+ } = options;
15
+
16
+ const config: ConfigWithExtends[] = [{
17
+ name: "@workleap/eslint-configs/jsx-a11y",
18
+ files: [
19
+ "**/*.{js,ts,jsx,tsx,cjs,mjs}"
20
+ ],
21
+ extends: [
22
+ jsxA11yPlugin.flatConfigs.recommended
23
+ ],
24
+ languageOptions: {
25
+ parserOptions: {
26
+ ecmaFeatures: {
27
+ jsx: true
28
+ }
29
+ }
30
+ },
31
+ rules: {
32
+ // This rule ensures that all labels have an associated control that they are labeling.
33
+ // However, this rule causes a lot of false positive, since our current implementation of our company's design system
34
+ // does not use the "for" attribute in the label element and automatically add it inside Fields.
35
+ // Therefore, we are disabling this rule.
36
+ "jsx-a11y/label-has-associated-control:": "off",
37
+ // This rule ensures that all media elements have a <track> for captions.
38
+ // Since we don't use captions, we are disabling this rule.
39
+ "jsx-a11y/media-has-caption": "off",
40
+ // There is a really good article that describes the issues with autoFocus and why it should be avoided:
41
+ // https://brucelawson.co.uk/2009/the-accessibility-of-html-5-autofocus/
42
+ // However, this issue is with screen readers and not with keyboard navigation.
43
+ // In Workleap, we use autoFocus in a lot of places to improve the user experience.
44
+ // Therefore, we are disabling this rule.
45
+ "jsx-a11y/no-autofocus": "off",
46
+ // Positioned last to allow the consumer to override any rules.
47
+ ...rules
48
+ }
49
+ }];
50
+
51
+ return config;
52
+ };
@@ -0,0 +1,48 @@
1
+ import type { Linter } from "eslint";
2
+ import packageJsonPlugin from "eslint-plugin-package-json";
3
+ import type { ConfigWithExtends } from "./types.ts";
4
+
5
+ export interface PackageJsonConfigOptions {
6
+ rules?: Partial<Linter.RulesRecord>;
7
+ }
8
+
9
+ export const packageJsonGlobalIgnores = [];
10
+
11
+ export function packageJsonConfig(options: PackageJsonConfigOptions = {}) {
12
+ const {
13
+ rules = {}
14
+ } = options;
15
+
16
+ const config: ConfigWithExtends[] = [{
17
+ name: "@workleap/eslint-configs/package-json",
18
+ files: [
19
+ "**/package.json"
20
+ ],
21
+ extends: [
22
+ packageJsonPlugin.configs.recommended
23
+ ],
24
+ rules: {
25
+ "package-json/order-properties": "off",
26
+ "package-json/prefer-repository-shorthand": "off",
27
+ "package-json/sort-collections": [
28
+ "error",
29
+ [
30
+ // Do not sort "scripts".
31
+ "devDependencies",
32
+ "dependencies",
33
+ "peerDependencies",
34
+ "config"
35
+ ]
36
+ ],
37
+ // Doesn't support "workspace:*" at the moment.
38
+ "package-json/valid-package-def": "off",
39
+ // I am not sure why, this rule is triggering errors for valid paths.
40
+ "package-json/valid-repository-directory": "off",
41
+ "package-json/valid-scripts": "off",
42
+ // Positioned last to allow the consumer to override any rules.
43
+ ...rules
44
+ }
45
+ }];
46
+
47
+ return config;
48
+ };
@@ -0,0 +1,75 @@
1
+ import type { Rule } from "eslint";
2
+ import type ESTree from "estree";
3
+ import { basename, parse, sep } from "path";
4
+
5
+ export const sanitizePath = (filePath: string) => {
6
+ return filePath.replace(/\//g, sep).trim();
7
+ };
8
+
9
+ export const splitPath = (filePath: string) => {
10
+ return sanitizePath(filePath).split(sep);
11
+ };
12
+
13
+ export function getFilePath(context: Rule.RuleContext) {
14
+ return sanitizePath(context.getFilename());
15
+ }
16
+
17
+ export function getFileName(context: Rule.RuleContext) {
18
+ return basename(getFilePath(context));
19
+ }
20
+
21
+ const rule: Rule.RuleModule = {
22
+ meta: {
23
+ type: "suggestion",
24
+ docs: {
25
+ description: "CSS Modules should have the same name as a component and located in the same folder",
26
+ category: "Strict",
27
+ recommended: false,
28
+ url: "https://github.com/workleap/wl-web-configs/blob/main/packages/eslint-plugin/docs/rules/strict-css-modules-names.md"
29
+ }
30
+ },
31
+ create: function (context) {
32
+ const parsedPath = parse(getFileName(context));
33
+
34
+ const getNodeSource = (node: ESTree.ImportDeclaration) => {
35
+ return sanitizePath(node.source != null ? node.source.value as string : "");
36
+ };
37
+
38
+ const isCssModule = (source: string) => {
39
+ return source.endsWith(".module.css");
40
+ };
41
+
42
+ const isStylesheetInSameFolder = (source: string) => {
43
+ return splitPath(source).length <= 2; // ./myImage.svg
44
+ };
45
+
46
+ return {
47
+ ImportDeclaration: function (node) {
48
+ const importSource = getNodeSource(node);
49
+
50
+ if (isCssModule(importSource)) {
51
+ const validCssFilename = `${parsedPath.name}.module.css`;
52
+
53
+ if (!isStylesheetInSameFolder(importSource)) {
54
+ // ./myImage.svg
55
+ context.report({
56
+ node,
57
+ message: `CSS Modules should be associated to one component and located in the same folder ./${validCssFilename}. If the module is already used by another component, create a new one.`
58
+ });
59
+ } else {
60
+ const validCssPath = `.${sep}${validCssFilename}`;
61
+ const isNamingValid = importSource === validCssPath;
62
+ if (!isNamingValid) {
63
+ context.report({
64
+ node,
65
+ message: `CSS Modules should be associated to one component and should be named ./${validCssFilename}. If the module is already used by another component, create a new one.`
66
+ });
67
+ }
68
+ }
69
+ }
70
+ }
71
+ };
72
+ }
73
+ };
74
+
75
+ export { rule as strictCssModulesNamesRule };
@@ -0,0 +1,7 @@
1
+ import { strictCssModulesNamesRule } from "./strictCssModulesNames.ts";
2
+
3
+ export const WorkleapPlugin = {
4
+ rules: {
5
+ "strict-css-modules-names": strictCssModulesNamesRule
6
+ }
7
+ };
package/src/react.ts ADDED
@@ -0,0 +1,175 @@
1
+ import stylisticPlugin from "@stylistic/eslint-plugin";
2
+ import type { Linter } from "eslint";
3
+ import reactPlugin from "eslint-plugin-react";
4
+ import reactHooksPlugin from "eslint-plugin-react-hooks";
5
+ import type { ConfigWithExtends } from "./types.ts";
6
+
7
+ export interface ReactConfigOptions {
8
+ rules?: Partial<Linter.RulesRecord>;
9
+ }
10
+
11
+ export const reactGlobalIgnores = [];
12
+
13
+ export function reactConfig(options: ReactConfigOptions = {}) {
14
+ const {
15
+ rules = {}
16
+ } = options;
17
+
18
+ const config: ConfigWithExtends[] = [{
19
+ name: "@workleap/eslint-configs/react",
20
+ files: [
21
+ "**/*.[jt]sx"
22
+ ],
23
+ plugins: {
24
+ "@stylistic": stylisticPlugin
25
+ },
26
+ extends: [
27
+ reactPlugin.configs.flat.recommended,
28
+ // @ts-expect-error the typings are broken and think there's a ".default" to add.
29
+ reactHooksPlugin.configs.flat.recommended
30
+ ],
31
+ languageOptions: {
32
+ parserOptions: {
33
+ ecmaFeatures: {
34
+ jsx: true
35
+ }
36
+ }
37
+ },
38
+ settings: {
39
+ react: {
40
+ version: "detect"
41
+ }
42
+ },
43
+ rules: {
44
+ // React Recommend rules overrides
45
+ "react/display-name": "off",
46
+ "react/jsx-key": "off",
47
+ "react/jsx-no-duplicate-props": [
48
+ "warn",
49
+ {
50
+ ignoreCase: true
51
+ }
52
+ ],
53
+ "react/jsx-no-undef": [
54
+ "warn",
55
+ {
56
+ allowGlobals: true
57
+ }
58
+ ],
59
+ "react/no-unescaped-entities": "off",
60
+ "react/prop-types": "off",
61
+ "react/react-in-jsx-scope": "off",
62
+
63
+ // React extra rules
64
+ "react/button-has-type": "warn",
65
+ "react/default-props-match-prop-types": "warn",
66
+ "react/destructuring-assignment": [
67
+ "warn",
68
+ "always",
69
+ { ignoreClassFields: true }
70
+ ],
71
+ "react/forbid-foreign-prop-types": [
72
+ "warn",
73
+ {
74
+ allowInPropTypes: true
75
+ }
76
+ ],
77
+ "react/jsx-boolean-value": ["warn", "never"],
78
+ "react/jsx-filename-extension": [
79
+ "warn",
80
+ {
81
+ extensions: [".jsx", ".tsx"]
82
+ }
83
+ ],
84
+ "react/jsx-pascal-case": [
85
+ "warn",
86
+ {
87
+ allowAllCaps: true,
88
+ ignore: []
89
+ }
90
+ ],
91
+ "react/no-access-state-in-setstate": "warn",
92
+ "react/no-array-index-key": "warn",
93
+ "react/no-typos": "error",
94
+ "react/no-unused-prop-types": [
95
+ "warn",
96
+ {
97
+ customValidators: [],
98
+ skipShapeProps: true
99
+ }
100
+ ],
101
+ "react/no-unused-state": "warn",
102
+ "react/style-prop-object": "warn",
103
+
104
+ // React rules turned off in favor of @stylistic
105
+ "react/jsx-closing-bracket-location": "off",
106
+ "react/jsx-closing-tag-location": "off",
107
+ "react/jsx-curly-brace-presence": "off",
108
+ "react/jsx-curly-newline": "off",
109
+ "react/jsx-curly-spacing": "off",
110
+ "react/jsx-equals-spacing": "off",
111
+ "react/jsx-first-prop-new-line": "off",
112
+ "react/jsx-indent-props": "off",
113
+ "react/jsx-max-props-per-line": "off",
114
+ "react/jsx-one-expression-per-line": "off",
115
+ "react/jsx-tag-spacing": "off",
116
+ "react/jsx-wrap-multilines": "off",
117
+
118
+ // @stylistic rules (cannot use the recommended config" because it would conflict with the "typescript" config rules)
119
+ "@stylistic/jsx-closing-bracket-location": "warn",
120
+ "@stylistic/jsx-closing-tag-location": "warn",
121
+ "@stylistic/jsx-curly-brace-presence": [
122
+ "warn",
123
+ {
124
+ propElementValues: "always"
125
+ }
126
+ ],
127
+ "@stylistic/jsx-curly-newline": "warn",
128
+ "@stylistic/jsx-curly-spacing": [
129
+ "warn",
130
+ {
131
+ children: true,
132
+ when: "never"
133
+ }
134
+ ],
135
+ "@stylistic/jsx-equals-spacing": "warn",
136
+ "@stylistic/jsx-first-prop-new-line": "warn",
137
+ "@stylistic/jsx-function-call-newline": ["warn", "multiline"],
138
+ "@stylistic/jsx-max-props-per-line": [
139
+ "warn",
140
+ {
141
+ maximum: 1,
142
+ when: "multiline"
143
+ }
144
+ ],
145
+ "@stylistic/jsx-quotes": ["warn", "prefer-double"],
146
+ "@stylistic/jsx-tag-spacing": [
147
+ "warn",
148
+ {
149
+ // afterOpening: "never",
150
+ // beforeClosing: "never",
151
+ beforeSelfClosing: "always"
152
+ // closingSlash: "never"
153
+ }
154
+ ],
155
+ "@stylistic/jsx-wrap-multilines": [
156
+ "warn",
157
+ {
158
+ arrow: "parens-new-line",
159
+ assignment: "parens-new-line",
160
+ condition: "parens-new-line",
161
+ declaration: "parens-new-line",
162
+ logical: "parens-new-line",
163
+ prop: "parens-new-line",
164
+ propertyValue: "parens-new-line",
165
+ return: "parens-new-line"
166
+ }
167
+ ],
168
+
169
+ // Positioned last to allow the consumer to override any rules.
170
+ ...rules
171
+ }
172
+ }];
173
+
174
+ return config;
175
+ };
@@ -0,0 +1,53 @@
1
+ import type { Linter } from "eslint";
2
+ import storybookPlugin from "eslint-plugin-storybook";
3
+ import type { ConfigWithExtends } from "./types.ts";
4
+
5
+ export interface StorybookConfigOptions {
6
+ storiesRules?: Partial<Linter.RulesRecord>;
7
+ mainFileRules?: Partial<Linter.RulesRecord>;
8
+ }
9
+
10
+ export const storybookGlobalIgnores = [
11
+ "!.storybook",
12
+ "storybook-static"
13
+ ];
14
+
15
+ export function storybookConfig(options: StorybookConfigOptions = {}) {
16
+ const {
17
+ storiesRules = {},
18
+ mainFileRules = {}
19
+ } = options;
20
+
21
+ const config: ConfigWithExtends[] = [
22
+ {
23
+ name: "@workleap/eslint-configs/storybook-stories",
24
+ files: [
25
+ "**/*.{stories,storybook,story,chroma}.{js,ts,jsx,tsx}"
26
+ ],
27
+ extends: [
28
+ // @ts-expect-error the typings are broken and think there's a ".default" to add.
29
+ storybookPlugin.configs["flat/recommended"]
30
+ // // @ts-expect-error the types are broken and think there's a ".default" to add.
31
+ // storybookPlugin.configs["flat/csf"],
32
+ // // @ts-expect-error the types are broken and think there's a ".default" to add.
33
+ // storybookPlugin.configs["flat/csf-strict"]
34
+ ],
35
+ rules: storiesRules
36
+ },
37
+ {
38
+ name: "@workleap/eslint-configs/storybook-main",
39
+ files: ["**/{.storybook,storybook}/main.@(js|cjs|mjs|ts)"],
40
+ plugins: {
41
+ // @ts-expect-error the typings are broken.
42
+ storybook: storybookPlugin
43
+ },
44
+ rules: {
45
+ "storybook/no-uninstalled-addons": "warn",
46
+ // Positioned last to allow the consumer to override any rules.
47
+ ...mainFileRules
48
+ }
49
+ }
50
+ ];
51
+
52
+ return config;
53
+ };
@@ -0,0 +1,48 @@
1
+ import type { Linter } from "eslint";
2
+ import testingLibraryPlugin from "eslint-plugin-testing-library";
3
+ import type { ConfigWithExtends } from "./types.ts";
4
+
5
+ export interface TestingLibraryConfigOptions {
6
+ reactRules?: Partial<Linter.RulesRecord>;
7
+ jsRules?: Partial<Linter.RulesRecord>;
8
+ }
9
+
10
+ export const testingLibraryGlobalIgnores = [];
11
+
12
+ export function testingLibraryConfig(options: TestingLibraryConfigOptions = {}) {
13
+ const {
14
+ reactRules = {},
15
+ jsRules = {}
16
+ } = options;
17
+
18
+ const config: ConfigWithExtends[] = [
19
+ {
20
+ name: "@workleap/eslint-configs/testing-library-react",
21
+ files: [
22
+ "**/*.test.[jt]sx",
23
+ "**/*-test.[jt]sx",
24
+ "**/__tests__/*.[jt]sx",
25
+ "**/test.[jt]sx"
26
+ ],
27
+ extends: [
28
+ testingLibraryPlugin.configs["flat/react"]
29
+ ],
30
+ rules: reactRules
31
+ },
32
+ {
33
+ name: "@workleap/eslint-configs/testing-library-js",
34
+ files: [
35
+ "**/*.test.[jt]s",
36
+ "**/*-test.[jt]s",
37
+ "**/__tests__/*.[jt]s",
38
+ "**/test.[jt]s"
39
+ ],
40
+ extends: [
41
+ testingLibraryPlugin.configs["flat/dom"]
42
+ ],
43
+ rules: jsRules
44
+ }
45
+ ];
46
+
47
+ return config;
48
+ };
package/src/types.ts ADDED
@@ -0,0 +1,27 @@
1
+ import type { Linter } from "eslint";
2
+
3
+ // Copied from: https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/types.ts
4
+ /**
5
+ * Infinite array type.
6
+ */
7
+ export type InfiniteArray<T> = T | InfiniteArray<T>[];
8
+
9
+ // Copied from: https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/types.ts
10
+ /**
11
+ * The type of array element in the `extends` property after flattening.
12
+ */
13
+ export type SimpleExtendsElement = string | Linter.Config;
14
+
15
+ // Copied from: https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/types.ts
16
+ /**
17
+ * The type of array element in the `extends` property before flattening.
18
+ */
19
+ export type ExtendsElement = SimpleExtendsElement | InfiniteArray<Linter.Config>;
20
+
21
+ // Copied from: https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/types.ts
22
+ /**
23
+ * Config with extends. Valid only inside of `defineConfig()`.
24
+ */
25
+ export interface ConfigWithExtends extends Linter.Config {
26
+ extends?: ExtendsElement[];
27
+ }
@@ -0,0 +1,133 @@
1
+ import js from "@eslint/js";
2
+ import stylisticPlugin from "@stylistic/eslint-plugin";
3
+ import type { Linter } from "eslint";
4
+ import tseslint from "typescript-eslint";
5
+ import type { ConfigWithExtends } from "./types.ts";
6
+
7
+ export interface TypescriptConfigOptions {
8
+ rules?: Partial<Linter.RulesRecord>;
9
+ }
10
+
11
+ export const typescriptGlobalIgnores = [];
12
+
13
+ export function typescriptConfig(tsconfigRootDir: string, options: TypescriptConfigOptions = {}) {
14
+ const {
15
+ rules = {}
16
+ } = options;
17
+
18
+ const config: ConfigWithExtends[] = [{
19
+ name: "@workleap/eslint-configs/typescript",
20
+ files: [
21
+ "**/*.{ts,tsx}"
22
+ ],
23
+ plugins: {
24
+ "@stylistic": stylisticPlugin
25
+ },
26
+ extends: [
27
+ js.configs.recommended,
28
+ tseslint.configs.recommendedTypeChecked,
29
+ tseslint.configs.stylisticTypeCheckedOnly,
30
+ stylisticPlugin.configs.customize({
31
+ braceStyle: "1tbs",
32
+ commaDangle: "never",
33
+ jsx: false,
34
+ quotes: "double",
35
+ semi: true,
36
+ severity: "warn"
37
+ })
38
+ ],
39
+ languageOptions: {
40
+ parser: tseslint.parser,
41
+ parserOptions: {
42
+ // Rely on TypeScript's project service to automatically discover the "tsconfig.json" file
43
+ // within the boundaries of "tsconfigRootDir".
44
+ projectService: true,
45
+ tsconfigRootDir
46
+ }
47
+ },
48
+ rules: {
49
+ // ESLint core rules overrides
50
+ "dot-notation": "off",
51
+ "indent": "off",
52
+ "no-dupe-class-members": "off",
53
+ "no-empty-function": "off",
54
+ "no-loop-func": "off",
55
+ "no-shadow": "off",
56
+ "no-unused-expressions": "off",
57
+ "no-use-before-define": "off",
58
+ "no-useless-constructor": "off",
59
+ "object-curly-spacing": "off",
60
+ "quotes": "off",
61
+ "semi": "off",
62
+
63
+ // ESlint deprecated core rules
64
+ "arrow-parens": "off",
65
+ "comma-dangle": "off",
66
+
67
+ // @typescript-eslint recommended rules overrides
68
+ "@typescript-eslint/dot-notation": "off",
69
+ "@typescript-eslint/no-base-to-string": "off",
70
+ "@typescript-eslint/no-empty-function": "off",
71
+ "@typescript-eslint/no-empty-object-type": [
72
+ "error",
73
+ {
74
+ allowInterfaces: "with-single-extends",
75
+ allowObjectTypes: "never"
76
+ }
77
+ ],
78
+ "@typescript-eslint/no-floating-promises": "off",
79
+ "@typescript-eslint/no-non-null-assertion": "off",
80
+ "@typescript-eslint/no-unsafe-argument": "off",
81
+ "@typescript-eslint/no-unsafe-assignment": "off",
82
+ "@typescript-eslint/no-unsafe-member-access": "off",
83
+ "@typescript-eslint/only-throw-error": "off",
84
+ "@typescript-eslint/prefer-nullish-coalescing": "off",
85
+ "@typescript-eslint/restrict-template-expressions": "off",
86
+
87
+ // @stylistic recommend rules overrides
88
+ "@stylistic/arrow-parens": [
89
+ "warn",
90
+ "as-needed",
91
+ {
92
+ requireForBlockBody: false
93
+ }
94
+ ],
95
+ "@stylistic/indent": [
96
+ "warn",
97
+ 4,
98
+ {
99
+ SwitchCase: 1,
100
+ CallExpression: { arguments: "first" }
101
+ }
102
+ ],
103
+ "@stylistic/indent-binary-ops": ["warn", 4],
104
+ "@stylistic/member-delimiter-style": [
105
+ "warn",
106
+ {
107
+ multiline: {
108
+ delimiter: "semi"
109
+ },
110
+ singleline: {
111
+ delimiter: "semi"
112
+ }
113
+ }
114
+ ],
115
+ "@stylistic/multiline-ternary": "off",
116
+ "@stylistic/no-multiple-empty-lines": [
117
+ "warn",
118
+ {
119
+ // View https://eslint.style/rules/eol-last.
120
+ max: 1
121
+ }
122
+ ],
123
+ "@stylistic/operator-linebreak": "off",
124
+ // Should be the default but somehow it's not enforced if it's not explicitly specified.
125
+ "@stylistic/quote-props": "off",
126
+
127
+ // Positioned last to allow the consumer to override any rules.
128
+ ...rules
129
+ }
130
+ }];
131
+
132
+ return config;
133
+ };