@baseplate-dev/tools 0.2.1 → 0.2.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/eslint-configs/react.js
CHANGED
|
@@ -42,6 +42,13 @@ export const reactEslintConfig = tsEslint.config(
|
|
|
42
42
|
'error',
|
|
43
43
|
{ checksVoidReturn: { attributes: false } },
|
|
44
44
|
],
|
|
45
|
+
// Allow floating navigate from useNavigate to be handled by the router
|
|
46
|
+
'@typescript-eslint/no-floating-promises': [
|
|
47
|
+
'error',
|
|
48
|
+
{
|
|
49
|
+
allowForKnownSafeCalls: ['UseNavigateResult'],
|
|
50
|
+
},
|
|
51
|
+
],
|
|
45
52
|
},
|
|
46
53
|
},
|
|
47
54
|
|
|
@@ -56,6 +63,21 @@ export const reactEslintConfig = tsEslint.config(
|
|
|
56
63
|
rules: {
|
|
57
64
|
// We use replace since it is not supported by ES2020
|
|
58
65
|
'unicorn/prefer-string-replace-all': 'off',
|
|
66
|
+
// Support kebab case with - prefix to support ignored files in routes
|
|
67
|
+
'unicorn/filename-case': [
|
|
68
|
+
'error',
|
|
69
|
+
{
|
|
70
|
+
cases: {
|
|
71
|
+
kebabCase: true,
|
|
72
|
+
},
|
|
73
|
+
ignore: [String.raw`^-[a-z0-9\-\.]+$`],
|
|
74
|
+
},
|
|
75
|
+
],
|
|
59
76
|
},
|
|
60
77
|
},
|
|
78
|
+
|
|
79
|
+
// Global ignores
|
|
80
|
+
{
|
|
81
|
+
ignores: ['**/route-tree.gen.ts'],
|
|
82
|
+
},
|
|
61
83
|
);
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ESLint rule to disallow unused dependencies in `createGeneratorTask`'s `dependencies` object.
|
|
5
|
+
*
|
|
6
|
+
* This rule checks if a dependency declared in the `dependencies` object of a `createGeneratorTask`
|
|
7
|
+
* is actually destructured and used in the `run` function's parameters.
|
|
8
|
+
* It assumes that `no-unused-vars` will catch any unused destructured variables within the `run` function body.
|
|
9
|
+
*
|
|
10
|
+
* @type {import('eslint').Rule.RuleModule}
|
|
11
|
+
*/
|
|
12
|
+
export default {
|
|
13
|
+
meta: {
|
|
14
|
+
type: 'suggestion',
|
|
15
|
+
docs: {
|
|
16
|
+
description:
|
|
17
|
+
'Disallow unused dependencies in createGeneratorTask dependencies object.',
|
|
18
|
+
category: 'Possible Errors',
|
|
19
|
+
recommended: false,
|
|
20
|
+
},
|
|
21
|
+
fixable: 'code', // Indicate that this rule is auto-fixable
|
|
22
|
+
schema: [], // No options for this rule
|
|
23
|
+
messages: {
|
|
24
|
+
unusedDependency:
|
|
25
|
+
'Dependency "{{dependencyName}}" declared but not destructured in run function.',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
create(context) {
|
|
29
|
+
return {
|
|
30
|
+
CallExpression(node) {
|
|
31
|
+
// Check if this is a call to `createGeneratorTask`
|
|
32
|
+
// The callee can be `createGeneratorTask` directly or `someObject.createGeneratorTask`
|
|
33
|
+
const callee = node.callee;
|
|
34
|
+
let isCreateGeneratorTaskCall = false;
|
|
35
|
+
|
|
36
|
+
if (
|
|
37
|
+
callee.type === 'Identifier' &&
|
|
38
|
+
callee.name === 'createGeneratorTask'
|
|
39
|
+
) {
|
|
40
|
+
isCreateGeneratorTaskCall = true;
|
|
41
|
+
} else if (
|
|
42
|
+
callee.type === 'MemberExpression' &&
|
|
43
|
+
callee.property.type === 'Identifier' &&
|
|
44
|
+
callee.property.name === 'createGeneratorTask'
|
|
45
|
+
) {
|
|
46
|
+
isCreateGeneratorTaskCall = true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (
|
|
50
|
+
isCreateGeneratorTaskCall &&
|
|
51
|
+
node.arguments.length > 0 &&
|
|
52
|
+
node.arguments[0].type === 'ObjectExpression'
|
|
53
|
+
) {
|
|
54
|
+
const taskConfig = node.arguments[0];
|
|
55
|
+
let dependenciesNode = null;
|
|
56
|
+
let runFunctionNode = null;
|
|
57
|
+
|
|
58
|
+
// Find 'dependencies' and 'run' properties within the task configuration object
|
|
59
|
+
for (const prop of taskConfig.properties) {
|
|
60
|
+
if (prop.type === 'Property' && prop.key.type === 'Identifier') {
|
|
61
|
+
if (
|
|
62
|
+
prop.key.name === 'dependencies' &&
|
|
63
|
+
prop.value.type === 'ObjectExpression'
|
|
64
|
+
) {
|
|
65
|
+
dependenciesNode = prop.value;
|
|
66
|
+
} else if (
|
|
67
|
+
prop.key.name === 'run' &&
|
|
68
|
+
(prop.value.type === 'FunctionExpression' ||
|
|
69
|
+
prop.value.type === 'ArrowFunctionExpression')
|
|
70
|
+
) {
|
|
71
|
+
runFunctionNode = prop.value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (dependenciesNode && runFunctionNode) {
|
|
77
|
+
const destructuredRunParams = new Set();
|
|
78
|
+
// Check if the run function has parameters and if the first one is an ObjectPattern (destructuring)
|
|
79
|
+
if (
|
|
80
|
+
runFunctionNode.params.length > 0 &&
|
|
81
|
+
runFunctionNode.params[0].type === 'ObjectPattern'
|
|
82
|
+
) {
|
|
83
|
+
for (const paramProp of runFunctionNode.params[0].properties) {
|
|
84
|
+
// Ensure it's a simple property (e.g., { foo }) not a rest element or nested destructuring
|
|
85
|
+
if (
|
|
86
|
+
paramProp.type === 'Property' &&
|
|
87
|
+
paramProp.key.type === 'Identifier'
|
|
88
|
+
) {
|
|
89
|
+
destructuredRunParams.add(paramProp.key.name);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Iterate over declared dependencies and check if they are destructured in the run function
|
|
95
|
+
for (const depProp of dependenciesNode.properties) {
|
|
96
|
+
if (
|
|
97
|
+
depProp.type === 'Property' &&
|
|
98
|
+
depProp.key.type === 'Identifier'
|
|
99
|
+
) {
|
|
100
|
+
const dependencyName = depProp.key.name;
|
|
101
|
+
|
|
102
|
+
// If the dependency is declared but NOT found in the destructured parameters of the run function
|
|
103
|
+
if (!destructuredRunParams.has(dependencyName)) {
|
|
104
|
+
context.report({
|
|
105
|
+
node: depProp, // Report on the specific dependency property
|
|
106
|
+
messageId: 'unusedDependency',
|
|
107
|
+
data: { dependencyName },
|
|
108
|
+
fix(fixer) {
|
|
109
|
+
// Remove the entire property from the dependencies object
|
|
110
|
+
// This handles commas and whitespace correctly for object properties.
|
|
111
|
+
const sourceCode = context.sourceCode;
|
|
112
|
+
const nextToken = sourceCode.getTokenAfter(depProp);
|
|
113
|
+
|
|
114
|
+
// If there's a comma after this property, remove it too
|
|
115
|
+
if (
|
|
116
|
+
nextToken &&
|
|
117
|
+
nextToken.type === 'Punctuator' &&
|
|
118
|
+
nextToken.value === ',' &&
|
|
119
|
+
depProp.range
|
|
120
|
+
) {
|
|
121
|
+
return fixer.removeRange([
|
|
122
|
+
depProp.range[0],
|
|
123
|
+
nextToken.range[1],
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return fixer.remove(depProp);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
};
|
|
@@ -5,8 +5,11 @@ import vitest from '@vitest/eslint-plugin';
|
|
|
5
5
|
import { importX } from 'eslint-plugin-import-x';
|
|
6
6
|
import perfectionist from 'eslint-plugin-perfectionist';
|
|
7
7
|
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
|
|
8
|
+
import unusedImports from 'eslint-plugin-unused-imports';
|
|
8
9
|
import tsEslint from 'typescript-eslint';
|
|
9
10
|
|
|
11
|
+
import noUnusedGeneratorDependencies from './rules/no-unused-generator-dependencies.js';
|
|
12
|
+
|
|
10
13
|
/**
|
|
11
14
|
* @typedef {Object} GenerateTypescriptEslintConfigOptions
|
|
12
15
|
* @property {string[]} [extraTsFileGlobs] - Additional file globs to lint with Typescript
|
|
@@ -113,6 +116,34 @@ export function generateTypescriptEslintConfig(options = []) {
|
|
|
113
116
|
'error',
|
|
114
117
|
{ ignoreTernaryTests: true },
|
|
115
118
|
],
|
|
119
|
+
|
|
120
|
+
// Allow redirect and notFound to be thrown from routes (placing in generic config to allow it to be used in *.ts files too)
|
|
121
|
+
'@typescript-eslint/only-throw-error': [
|
|
122
|
+
'error',
|
|
123
|
+
{
|
|
124
|
+
allow: ['NotFoundError', 'Redirect'],
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
// Unused Imports Configs
|
|
131
|
+
{
|
|
132
|
+
plugins: {
|
|
133
|
+
'unused-imports': unusedImports,
|
|
134
|
+
},
|
|
135
|
+
rules: {
|
|
136
|
+
'@typescript-eslint/no-unused-vars': 'off',
|
|
137
|
+
'unused-imports/no-unused-imports': 'error',
|
|
138
|
+
'unused-imports/no-unused-vars': [
|
|
139
|
+
'error',
|
|
140
|
+
{
|
|
141
|
+
vars: 'all',
|
|
142
|
+
varsIgnorePattern: '^_',
|
|
143
|
+
args: 'after-used',
|
|
144
|
+
argsIgnorePattern: '^_',
|
|
145
|
+
},
|
|
146
|
+
],
|
|
116
147
|
},
|
|
117
148
|
},
|
|
118
149
|
|
|
@@ -236,6 +267,21 @@ export function generateTypescriptEslintConfig(options = []) {
|
|
|
236
267
|
},
|
|
237
268
|
},
|
|
238
269
|
|
|
270
|
+
// Baseplate Generator Eslint Rule
|
|
271
|
+
{
|
|
272
|
+
files: ['**/*.generator.ts'],
|
|
273
|
+
plugins: {
|
|
274
|
+
baseplate: {
|
|
275
|
+
rules: {
|
|
276
|
+
'no-unused-generator-dependencies': noUnusedGeneratorDependencies,
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
rules: {
|
|
281
|
+
'baseplate/no-unused-generator-dependencies': ['error'],
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
|
|
239
285
|
// Global Ignores
|
|
240
286
|
{ ignores: ['dist', 'node_modules'] },
|
|
241
287
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baseplate-dev/tools",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Shared dev configurations for linting, formatting, and testing Baseplate projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"development-tools",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"eslint-plugin-react-hooks": "5.2.0",
|
|
64
64
|
"eslint-plugin-storybook": "0.12.0",
|
|
65
65
|
"eslint-plugin-unicorn": "59.0.1",
|
|
66
|
+
"eslint-plugin-unused-imports": "4.1.4",
|
|
66
67
|
"prettier-plugin-packagejson": "2.5.11",
|
|
67
68
|
"prettier-plugin-tailwindcss": "0.6.11",
|
|
68
69
|
"typescript-eslint": "8.32.0"
|