@baseplate-dev/tools 0.5.2 → 0.5.3
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/eslint-configs/react.js +113 -69
- package/eslint-configs/storybook.js +11 -17
- package/eslint-configs/typescript.js +10 -9
- package/eslint.config.node.js +18 -13
- package/eslint.config.react.js +42 -15
- package/package.json +3 -2
- package/prettier.config.react.js +4 -1
package/eslint-configs/react.js
CHANGED
|
@@ -4,94 +4,138 @@
|
|
|
4
4
|
* @typedef {import('./typescript.js').GenerateTypescriptEslintConfigOptions} GenerateTypescriptEslintConfigOptions
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} GenerateReactEslintConfigOptions
|
|
9
|
+
* @property {string | null} [tailwindEntryPoint] - Absolute path to Tailwind CSS entry file (e.g., path.join(import.meta.dirname, 'src/styles.css')). Pass null to disable Tailwind linting.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import eslintPluginBetterTailwindcss from 'eslint-plugin-better-tailwindcss';
|
|
7
13
|
import eslintPluginImportX from 'eslint-plugin-import-x';
|
|
8
14
|
import reactJsxA11yPlugin from 'eslint-plugin-jsx-a11y';
|
|
9
15
|
import reactPlugin from 'eslint-plugin-react';
|
|
10
16
|
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
|
17
|
+
import { defineConfig } from 'eslint/config';
|
|
11
18
|
import globals from 'globals';
|
|
12
|
-
import tsEslint from 'typescript-eslint';
|
|
13
19
|
|
|
14
20
|
/** @type {GenerateTypescriptEslintConfigOptions} */
|
|
15
21
|
export const reactTypescriptEslintOptions = {
|
|
16
22
|
extraDefaultProjectFiles: [],
|
|
17
23
|
};
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
],
|
|
31
|
-
settings: {
|
|
32
|
-
react: {
|
|
33
|
-
version: 'detect',
|
|
25
|
+
/**
|
|
26
|
+
* Generates a React ESLint configuration
|
|
27
|
+
* @param {GenerateReactEslintConfigOptions} options - Configuration options
|
|
28
|
+
*/
|
|
29
|
+
export function generateReactEslintConfig(options) {
|
|
30
|
+
return defineConfig(
|
|
31
|
+
// React & A11y
|
|
32
|
+
{
|
|
33
|
+
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
|
|
34
|
+
languageOptions: {
|
|
35
|
+
globals: { ...globals.browser },
|
|
34
36
|
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
{
|
|
40
|
-
files: ['**/*.{tsx,mtsx}'],
|
|
41
|
-
rules: {
|
|
42
|
-
// Allow promises to be returned from functions for attributes in React
|
|
43
|
-
// to allow for React Hook Form handleSubmit to work as expected
|
|
44
|
-
// See https://github.com/orgs/react-hook-form/discussions/8020
|
|
45
|
-
'@typescript-eslint/no-misused-promises': [
|
|
46
|
-
'error',
|
|
47
|
-
{ checksVoidReturn: { attributes: false } },
|
|
37
|
+
extends: [
|
|
38
|
+
reactPlugin.configs.flat?.recommended,
|
|
39
|
+
reactPlugin.configs.flat?.['jsx-runtime'],
|
|
40
|
+
reactJsxA11yPlugin.flatConfigs.recommended,
|
|
48
41
|
],
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
allowForKnownSafeCalls: ['UseNavigateResult'],
|
|
42
|
+
settings: {
|
|
43
|
+
react: {
|
|
44
|
+
version: 'detect',
|
|
54
45
|
},
|
|
55
|
-
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// Typescript
|
|
50
|
+
{
|
|
51
|
+
files: ['**/*.{tsx,mtsx}'],
|
|
52
|
+
rules: {
|
|
53
|
+
// Allow promises to be returned from functions for attributes in React
|
|
54
|
+
// to allow for React Hook Form handleSubmit to work as expected
|
|
55
|
+
// See https://github.com/orgs/react-hook-form/discussions/8020
|
|
56
|
+
'@typescript-eslint/no-misused-promises': [
|
|
57
|
+
'error',
|
|
58
|
+
{ checksVoidReturn: { attributes: false } },
|
|
59
|
+
],
|
|
60
|
+
// Allow floating navigate from useNavigate to be handled by the router
|
|
61
|
+
'@typescript-eslint/no-floating-promises': [
|
|
62
|
+
'error',
|
|
63
|
+
{
|
|
64
|
+
allowForKnownSafeCalls: ['UseNavigateResult'],
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
56
68
|
},
|
|
57
|
-
},
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
// React Hooks
|
|
71
|
+
reactHooksPlugin.configs.flat['recommended-latest'],
|
|
72
|
+
{
|
|
73
|
+
rules: {
|
|
74
|
+
// Disable new strict rules from react-hooks v7 until we enable React Compiler
|
|
75
|
+
'react-hooks/refs': 'off',
|
|
76
|
+
'react-hooks/set-state-in-effect': 'off',
|
|
77
|
+
'react-hooks/preserve-manual-memoization': 'off',
|
|
78
|
+
'react-hooks/incompatible-library': 'off',
|
|
79
|
+
},
|
|
68
80
|
},
|
|
69
|
-
},
|
|
70
81
|
|
|
71
|
-
|
|
72
|
-
|
|
82
|
+
// Import-X
|
|
83
|
+
// @ts-ignore - bug with incompatible types between @types/eslint and typescript eslint config - https://github.com/un-ts/eslint-plugin-import-x/issues/421
|
|
84
|
+
eslintPluginImportX.flatConfigs.react,
|
|
73
85
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
// Better Tailwindcss - correctness rules only (formatting handled by Prettier)
|
|
87
|
+
// Only enable if tailwindEntryPoint is provided (not null/undefined)
|
|
88
|
+
...(options.tailwindEntryPoint !== null &&
|
|
89
|
+
options.tailwindEntryPoint !== undefined
|
|
90
|
+
? [
|
|
91
|
+
eslintPluginBetterTailwindcss.configs['correctness'],
|
|
92
|
+
{
|
|
93
|
+
settings: {
|
|
94
|
+
'better-tailwindcss': {
|
|
95
|
+
entryPoint: options.tailwindEntryPoint,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
83
98
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
{
|
|
100
|
+
rules: {
|
|
101
|
+
// Detect custom component classes defined in @layer components
|
|
102
|
+
'better-tailwindcss/no-unknown-classes': [
|
|
103
|
+
'error',
|
|
104
|
+
{
|
|
105
|
+
detectComponentClasses: true,
|
|
106
|
+
// Ignore classes used in custom component classes
|
|
107
|
+
ignore: [
|
|
108
|
+
'toaster', // Sonner library class
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
]
|
|
115
|
+
: []),
|
|
116
|
+
|
|
117
|
+
// Unicorn
|
|
118
|
+
{
|
|
119
|
+
rules: {
|
|
120
|
+
// Support kebab case with - prefix to support ignored files in routes and $ prefix for Tanstack camelCase files
|
|
121
|
+
'unicorn/filename-case': [
|
|
122
|
+
'error',
|
|
123
|
+
{
|
|
124
|
+
cases: {
|
|
125
|
+
kebabCase: true,
|
|
126
|
+
},
|
|
127
|
+
ignore: [
|
|
128
|
+
String.raw`^-[a-z0-9\-\.]+$`,
|
|
129
|
+
String.raw`^\$[a-zA-Z0-9\.]+$`,
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
},
|
|
90
134
|
},
|
|
91
|
-
},
|
|
92
135
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
);
|
|
136
|
+
// Global ignores
|
|
137
|
+
{
|
|
138
|
+
ignores: ['**/route-tree.gen.ts'],
|
|
139
|
+
},
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {import('./typescript.js').GenerateTypescriptEslintConfigOptions} GenerateTypescriptEslintConfigOptions
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
3
|
import storybook from 'eslint-plugin-storybook';
|
|
8
|
-
import
|
|
4
|
+
import { defineConfig } from 'eslint/config';
|
|
9
5
|
|
|
10
|
-
/** @type {
|
|
11
|
-
export const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
],
|
|
20
|
-
};
|
|
6
|
+
/** @type {string[]} */
|
|
7
|
+
export const storybookTypescriptExtraDevDependencies = [
|
|
8
|
+
// allow dev dependencies for Storybook configuration block
|
|
9
|
+
'.storybook/**/*.{js,ts,tsx,jsx}',
|
|
10
|
+
// allow dev dependencies for Storybook
|
|
11
|
+
'**/*.stories.{js,ts,tsx,jsx}',
|
|
12
|
+
// allow dev dependencies for MDX files
|
|
13
|
+
'**/*.mdx',
|
|
14
|
+
];
|
|
21
15
|
|
|
22
|
-
export const storybookEslintConfig =
|
|
16
|
+
export const storybookEslintConfig = defineConfig(
|
|
23
17
|
// Storybook
|
|
24
18
|
// @ts-ignore -- TypeScript resolution bug where it expects a named export called default
|
|
25
19
|
storybook.configs['flat/recommended'],
|
|
@@ -6,6 +6,7 @@ import { importX } from 'eslint-plugin-import-x';
|
|
|
6
6
|
import perfectionist from 'eslint-plugin-perfectionist';
|
|
7
7
|
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
|
|
8
8
|
import unusedImports from 'eslint-plugin-unused-imports';
|
|
9
|
+
import { defineConfig } from 'eslint/config';
|
|
9
10
|
import globals from 'globals';
|
|
10
11
|
import tsEslint from 'typescript-eslint';
|
|
11
12
|
|
|
@@ -13,6 +14,7 @@ import noUnusedGeneratorDependencies from './rules/no-unused-generator-dependenc
|
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @typedef {Object} GenerateTypescriptEslintConfigOptions
|
|
17
|
+
* @property {string} [rootDir] - Absolute path to tsconfig root directory (for tsconfigRootDir)
|
|
16
18
|
* @property {string[]} [extraTsFileGlobs] - Additional file globs to lint with Typescript
|
|
17
19
|
* @property {string[]} [extraDevDependencies] - Additional globs for dev dependencies
|
|
18
20
|
* @property {string[]} [extraDefaultProjectFiles] - Additional default project files
|
|
@@ -23,13 +25,10 @@ const KEEP_UNUSED_IMPORTS =
|
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
27
|
* Generates a Typescript ESLint configuration
|
|
26
|
-
* @param {GenerateTypescriptEslintConfigOptions
|
|
28
|
+
* @param {GenerateTypescriptEslintConfigOptions} [options={}] - Configuration options
|
|
27
29
|
*/
|
|
28
|
-
export function generateTypescriptEslintConfig(options =
|
|
29
|
-
const tsFileGlobs = [
|
|
30
|
-
'**/*.{ts,tsx}',
|
|
31
|
-
...options.flatMap((option) => option.extraTsFileGlobs ?? []),
|
|
32
|
-
];
|
|
30
|
+
export function generateTypescriptEslintConfig(options = {}) {
|
|
31
|
+
const tsFileGlobs = ['**/*.{ts,tsx}', ...(options.extraTsFileGlobs ?? [])];
|
|
33
32
|
const devDependencies = [
|
|
34
33
|
// allow dev dependencies for test files
|
|
35
34
|
'**/*.test-helper.{js,ts,jsx,tsx}',
|
|
@@ -42,13 +41,13 @@ export function generateTypescriptEslintConfig(options = []) {
|
|
|
42
41
|
'*.{js,ts}',
|
|
43
42
|
'.*.{js,ts}',
|
|
44
43
|
'.workspace-meta/**/*',
|
|
45
|
-
...options.
|
|
44
|
+
...(options.extraDevDependencies ?? []),
|
|
46
45
|
];
|
|
47
46
|
const defaultProjectFiles = [
|
|
48
47
|
'vitest.config.ts',
|
|
49
|
-
...options.
|
|
48
|
+
...(options.extraDefaultProjectFiles ?? []),
|
|
50
49
|
];
|
|
51
|
-
return
|
|
50
|
+
return defineConfig(
|
|
52
51
|
// ESLint Configs for all files
|
|
53
52
|
eslint.configs.recommended,
|
|
54
53
|
{
|
|
@@ -82,6 +81,7 @@ export function generateTypescriptEslintConfig(options = []) {
|
|
|
82
81
|
],
|
|
83
82
|
languageOptions: {
|
|
84
83
|
parserOptions: {
|
|
84
|
+
...(options.rootDir ? { tsconfigRootDir: options.rootDir } : {}),
|
|
85
85
|
projectService: {
|
|
86
86
|
// allow default project for root configs
|
|
87
87
|
allowDefaultProject: defaultProjectFiles,
|
|
@@ -162,6 +162,7 @@ export function generateTypescriptEslintConfig(options = []) {
|
|
|
162
162
|
},
|
|
163
163
|
|
|
164
164
|
// Import-X Configs
|
|
165
|
+
// @ts-ignore - bug with incompatible types between @types/eslint and typescript eslint config - https://github.com/un-ts/eslint-plugin-import-x/issues/421
|
|
165
166
|
importX.flatConfigs.recommended,
|
|
166
167
|
importX.flatConfigs.typescript,
|
|
167
168
|
{
|
package/eslint.config.node.js
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { defineConfig } from 'eslint/config';
|
|
4
4
|
|
|
5
5
|
import { prettierEslintConfig } from './eslint-configs/prettier.js';
|
|
6
6
|
import { generateTypescriptEslintConfig } from './eslint-configs/typescript.js';
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {Object} DefineNodeEslintConfigOptions
|
|
10
|
+
* @property {string} dirname - The import.meta.dirname of the calling package
|
|
11
|
+
* @property {string[]} [extraDefaultProjectFiles] - Additional default project files
|
|
12
|
+
* @property {string[]} [ignores] - Additional ignore patterns
|
|
13
|
+
*/
|
|
9
14
|
|
|
10
15
|
/**
|
|
11
|
-
*
|
|
12
|
-
* @param {
|
|
16
|
+
* Defines a Node.js ESLint configuration for a package
|
|
17
|
+
* @param {DefineNodeEslintConfigOptions} options - Configuration options
|
|
13
18
|
*/
|
|
14
|
-
export function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
export function defineNodeEslintConfig(options) {
|
|
20
|
+
const { dirname, extraDefaultProjectFiles = [], ignores = [] } = options;
|
|
21
|
+
|
|
22
|
+
return defineConfig(
|
|
23
|
+
...generateTypescriptEslintConfig({
|
|
24
|
+
rootDir: dirname,
|
|
25
|
+
extraDefaultProjectFiles,
|
|
26
|
+
}),
|
|
21
27
|
prettierEslintConfig,
|
|
28
|
+
...(ignores.length > 0 ? [{ ignores }] : []),
|
|
22
29
|
);
|
|
23
30
|
}
|
|
24
|
-
|
|
25
|
-
export default generateNodeConfig();
|
package/eslint.config.react.js
CHANGED
|
@@ -1,24 +1,51 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { defineConfig } from 'eslint/config';
|
|
4
|
+
import path from 'node:path';
|
|
4
5
|
|
|
5
6
|
import { prettierEslintConfig } from './eslint-configs/prettier.js';
|
|
6
|
-
import {
|
|
7
|
-
reactEslintConfig,
|
|
8
|
-
reactTypescriptEslintOptions,
|
|
9
|
-
} from './eslint-configs/react.js';
|
|
7
|
+
import { generateReactEslintConfig } from './eslint-configs/react.js';
|
|
10
8
|
import {
|
|
11
9
|
storybookEslintConfig,
|
|
12
|
-
|
|
10
|
+
storybookTypescriptExtraDevDependencies,
|
|
13
11
|
} from './eslint-configs/storybook.js';
|
|
14
12
|
import { generateTypescriptEslintConfig } from './eslint-configs/typescript.js';
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} DefineReactEslintConfigOptions
|
|
16
|
+
* @property {string} dirname - The import.meta.dirname of the calling package
|
|
17
|
+
* @property {boolean} [includeStorybook=false] - Whether to include Storybook configuration
|
|
18
|
+
* @property {string | null} [tailwindEntryPoint='src/styles.css'] - Path to Tailwind CSS entry file relative to dirname. Pass null to disable Tailwind linting.
|
|
19
|
+
* @property {string[]} [ignores] - Additional ignore patterns
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Defines a React ESLint configuration for a package
|
|
24
|
+
* @param {DefineReactEslintConfigOptions} options - Configuration options
|
|
25
|
+
*/
|
|
26
|
+
export function defineReactEslintConfig(options) {
|
|
27
|
+
const {
|
|
28
|
+
dirname,
|
|
29
|
+
includeStorybook = false,
|
|
30
|
+
tailwindEntryPoint = 'src/styles.css',
|
|
31
|
+
ignores = [],
|
|
32
|
+
} = options;
|
|
33
|
+
|
|
34
|
+
const absoluteTailwindPath =
|
|
35
|
+
tailwindEntryPoint === null ? null : path.join(dirname, tailwindEntryPoint);
|
|
36
|
+
|
|
37
|
+
return defineConfig(
|
|
38
|
+
...generateTypescriptEslintConfig({
|
|
39
|
+
rootDir: dirname,
|
|
40
|
+
extraDevDependencies: includeStorybook
|
|
41
|
+
? storybookTypescriptExtraDevDependencies
|
|
42
|
+
: [],
|
|
43
|
+
}),
|
|
44
|
+
...generateReactEslintConfig({
|
|
45
|
+
tailwindEntryPoint: absoluteTailwindPath,
|
|
46
|
+
}),
|
|
47
|
+
...(includeStorybook ? storybookEslintConfig : []),
|
|
48
|
+
prettierEslintConfig,
|
|
49
|
+
...(ignores.length > 0 ? [{ ignores }] : []),
|
|
50
|
+
);
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baseplate-dev/tools",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Shared dev configurations for linting, formatting, and testing Baseplate projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"development-tools",
|
|
@@ -52,9 +52,10 @@
|
|
|
52
52
|
"@eslint/js": "9.39.2",
|
|
53
53
|
"@tsconfig/node22": "22.0.5",
|
|
54
54
|
"@tsconfig/vite-react": "7.0.2",
|
|
55
|
-
"@vitest/eslint-plugin": "1.6.
|
|
55
|
+
"@vitest/eslint-plugin": "1.6.9",
|
|
56
56
|
"eslint-config-prettier": "10.1.8",
|
|
57
57
|
"eslint-import-resolver-typescript": "4.4.4",
|
|
58
|
+
"eslint-plugin-better-tailwindcss": "4.2.0",
|
|
58
59
|
"eslint-plugin-import-x": "4.16.1",
|
|
59
60
|
"eslint-plugin-jsx-a11y": "6.10.2",
|
|
60
61
|
"eslint-plugin-perfectionist": "5.4.0",
|
package/prettier.config.react.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
+
|
|
2
3
|
import { fileURLToPath } from 'node:url';
|
|
3
4
|
|
|
4
5
|
import basePrettierConfig from './prettier.config.node.js';
|
|
@@ -11,11 +12,13 @@ import basePrettierConfig from './prettier.config.node.js';
|
|
|
11
12
|
/** @type {import("prettier").Config} */
|
|
12
13
|
export default {
|
|
13
14
|
...basePrettierConfig,
|
|
15
|
+
// Tailwind-specific configuration
|
|
14
16
|
tailwindFunctions: ['clsx', 'cn', 'cva'],
|
|
15
17
|
tailwindStylesheet: './src/styles.css',
|
|
18
|
+
// Plugins must be specified as absolute paths due to Prettier limitations
|
|
19
|
+
// See: https://github.com/prettier/prettier/issues/8056
|
|
16
20
|
plugins: [
|
|
17
21
|
...(basePrettierConfig.plugins ?? []),
|
|
18
|
-
// workaround for this bug: https://github.com/prettier/prettier-vscode/issues/3641
|
|
19
22
|
fileURLToPath(import.meta.resolve('prettier-plugin-tailwindcss')),
|
|
20
23
|
],
|
|
21
24
|
};
|