@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.
- package/CHANGELOG.md +13 -0
- package/LICENSE +201 -0
- package/README.md +16 -0
- package/dist/by-project-type/defineMonorepoWorkspaceConfig.d.ts +15 -0
- package/dist/by-project-type/defineMonorepoWorkspaceConfig.js +51 -0
- package/dist/by-project-type/defineMonorepoWorkspaceConfig.js.map +1 -0
- package/dist/by-project-type/defineReactLibraryConfig.d.ts +27 -0
- package/dist/by-project-type/defineReactLibraryConfig.js +96 -0
- package/dist/by-project-type/defineReactLibraryConfig.js.map +1 -0
- package/dist/by-project-type/defineTypescriptLibraryConfig.d.ts +19 -0
- package/dist/by-project-type/defineTypescriptLibraryConfig.js +72 -0
- package/dist/by-project-type/defineTypescriptLibraryConfig.js.map +1 -0
- package/dist/by-project-type/defineWebApplicationConfig.d.ts +27 -0
- package/dist/by-project-type/defineWebApplicationConfig.js +96 -0
- package/dist/by-project-type/defineWebApplicationConfig.js.map +1 -0
- package/dist/core.d.ts +7 -0
- package/dist/core.js +184 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/jest.d.ts +7 -0
- package/dist/jest.js +55 -0
- package/dist/jest.js.map +1 -0
- package/dist/jsxAlly.d.ts +7 -0
- package/dist/jsxAlly.js +51 -0
- package/dist/jsxAlly.js.map +1 -0
- package/dist/packageJson.d.ts +7 -0
- package/dist/packageJson.js +47 -0
- package/dist/packageJson.js.map +1 -0
- package/dist/plugins/strictCssModulesNames.d.ts +7 -0
- package/dist/plugins/strictCssModulesNames.js +70 -0
- package/dist/plugins/strictCssModulesNames.js.map +1 -0
- package/dist/plugins/workleapPlugin.d.ts +5 -0
- package/dist/plugins/workleapPlugin.js +15 -0
- package/dist/plugins/workleapPlugin.js.map +1 -0
- package/dist/react.d.ts +7 -0
- package/dist/react.js +189 -0
- package/dist/react.js.map +1 -0
- package/dist/storybook.d.ts +8 -0
- package/dist/storybook.js +46 -0
- package/dist/storybook.js.map +1 -0
- package/dist/testingLibrary.d.ts +8 -0
- package/dist/testingLibrary.js +43 -0
- package/dist/testingLibrary.js.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/typescript.d.ts +7 -0
- package/dist/typescript.js +139 -0
- package/dist/typescript.js.map +1 -0
- package/dist/vitest.d.ts +7 -0
- package/dist/vitest.js +40 -0
- package/dist/vitest.js.map +1 -0
- package/dist/yaml.d.ts +7 -0
- package/dist/yaml.js +31 -0
- package/dist/yaml.js.map +1 -0
- package/package.json +82 -0
- package/src/by-project-type/defineMonorepoWorkspaceConfig.ts +45 -0
- package/src/by-project-type/defineReactLibraryConfig.ts +81 -0
- package/src/by-project-type/defineTypescriptLibraryConfig.ts +61 -0
- package/src/by-project-type/defineWebApplicationConfig.ts +114 -0
- package/src/core.ts +138 -0
- package/src/index.ts +16 -0
- package/src/jest.ts +53 -0
- package/src/jsxAlly.ts +52 -0
- package/src/packageJson.ts +48 -0
- package/src/plugins/strictCssModulesNames.ts +75 -0
- package/src/plugins/workleapPlugin.ts +7 -0
- package/src/react.ts +175 -0
- package/src/storybook.ts +53 -0
- package/src/testingLibrary.ts +48 -0
- package/src/types.ts +27 -0
- package/src/typescript.ts +133 -0
- package/src/vitest.ts +41 -0
- 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 };
|
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
|
+
};
|
package/src/storybook.ts
ADDED
|
@@ -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
|
+
};
|