@ethlete/eslint-plugin 1.0.0-next.13 → 1.0.0-next.15
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
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.0-next.15
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`6f4b966`](https://github.com/ethlete-io/ethdk/commit/6f4b966c4dc0244b9dfc40978f42362fe9c89a58) Thanks [@TomTomB](https://github.com/TomTomB)! - Expose the `recommendedSpec` flat config (relaxes `no-non-null-assertion` in `*.spec.ts`) as `ethlete.configs.recommendedSpec`.
|
|
8
|
+
|
|
9
|
+
## 1.0.0-next.14
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`423b30b`](https://github.com/ethlete-io/ethdk/commit/423b30ba4f368d7cee8c464ebd89b5df20d3934c) Thanks [@TomTomB](https://github.com/TomTomB)! - The `static` class member ban now excepts `ngTemplateContextGuard`, which Angular's template type checker requires to be static.
|
|
14
|
+
|
|
15
|
+
- [`7ca77d4`](https://github.com/ethlete-io/ethdk/commit/7ca77d4a9e1dd3abad7237227c62643159719b74) Thanks [@TomTomB](https://github.com/TomTomB)! - `no-trivial-return-type`: self-referencing (recursive) functions keep their return type annotation — TypeScript cannot infer a return type that depends on itself (TS7023), so the fixer no longer strips it there.
|
|
16
|
+
|
|
3
17
|
## 1.0.0-next.13
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -139,9 +139,10 @@ const recommendedTs = {
|
|
|
139
139
|
selector: 'FunctionExpression[async=true]',
|
|
140
140
|
message: 'No async/await. Use RxJS for all async operations.',
|
|
141
141
|
},
|
|
142
|
-
// No static class members
|
|
142
|
+
// No static class members — except ngTemplateContextGuard, which Angular's template
|
|
143
|
+
// type checker requires to be static (it types a structural directive's `let-` bindings)
|
|
143
144
|
{
|
|
144
|
-
selector:
|
|
145
|
+
selector: "MethodDefinition[static=true]:not([key.name='ngTemplateContextGuard'])",
|
|
145
146
|
message: 'No static class members.',
|
|
146
147
|
},
|
|
147
148
|
{
|
package/src/index.js
CHANGED
|
@@ -131,6 +131,8 @@ const configs = {
|
|
|
131
131
|
recommendedTs: recommendedTsWithPlugin,
|
|
132
132
|
/** Angular template rules (merged into a files: ['**\/*.html'] block) */
|
|
133
133
|
recommendedTemplate: recommendedTemplateWithPlugin,
|
|
134
|
+
/** Relaxed rules for spec files (files: ['**\/*.spec.ts']) */
|
|
135
|
+
recommendedSpec,
|
|
134
136
|
/**
|
|
135
137
|
* Both together as a flat array — the most common usage:
|
|
136
138
|
* export default [...baseConfig, ...ethlete.configs.recommended]
|
|
@@ -116,7 +116,6 @@ const buildObjectTextWithAppendedProperty = (sourceCode, objectExpression, entry
|
|
|
116
116
|
return existingText.length === 0 ? `{ ${entryText} }` : `{ ${existingText.join(', ')}, ${entryText} }`;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
const openingBrace = sourceCode.getFirstToken(objectExpression);
|
|
120
119
|
const closingBrace = sourceCode.getLastToken(objectExpression);
|
|
121
120
|
const closingIndent = closingBrace ? getIndent(sourceCode, closingBrace) : '';
|
|
122
121
|
const propertyIndent = properties[0] ? getIndent(sourceCode, properties[0]) : `${closingIndent} `;
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
*
|
|
12
12
|
* ALSO GOOD (non-trivial, keep it):
|
|
13
13
|
* const parse = (): Date => new Date(value)
|
|
14
|
+
*
|
|
15
|
+
* Self-referencing (recursive) functions are exempt: TypeScript cannot infer a
|
|
16
|
+
* return type that depends on itself (TS7023 under noImplicitAny), so the
|
|
17
|
+
* annotation is required there:
|
|
18
|
+
* const walk = (node): boolean => node.ok && walk(node.next)
|
|
14
19
|
*/
|
|
15
20
|
|
|
16
21
|
const TRIVIAL_TYPES = new Set([
|
|
@@ -69,6 +74,93 @@ const noTrivialReturnType = {
|
|
|
69
74
|
return false;
|
|
70
75
|
};
|
|
71
76
|
|
|
77
|
+
/**
|
|
78
|
+
* The names under which a function implementation can call itself: its own name
|
|
79
|
+
* (named function expression, or the variable an arrow is assigned to) and/or
|
|
80
|
+
* `this.<key>` for class/object methods and class-field arrows.
|
|
81
|
+
* @param {any} fn
|
|
82
|
+
*/
|
|
83
|
+
const selfReferenceNames = (fn) => {
|
|
84
|
+
/** @type {{ kind: 'name' | 'this', name: string }[]} */
|
|
85
|
+
const names = [];
|
|
86
|
+
|
|
87
|
+
if (fn.id && fn.id.type === 'Identifier') {
|
|
88
|
+
names.push({ kind: 'name', name: fn.id.name });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const parent = fn.parent;
|
|
92
|
+
|
|
93
|
+
if (parent) {
|
|
94
|
+
if (
|
|
95
|
+
(parent.type === 'MethodDefinition' || parent.type === 'PropertyDefinition' || parent.type === 'Property') &&
|
|
96
|
+
!parent.computed &&
|
|
97
|
+
parent.key &&
|
|
98
|
+
parent.key.type === 'Identifier'
|
|
99
|
+
) {
|
|
100
|
+
names.push({ kind: 'this', name: parent.key.name });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (parent.type === 'VariableDeclarator' && parent.id && parent.id.type === 'Identifier') {
|
|
104
|
+
names.push({ kind: 'name', name: parent.id.name });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return names;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Whether the function body references the function itself (recursion) — there the
|
|
113
|
+
* annotation is required, since TypeScript cannot infer a self-dependent return type.
|
|
114
|
+
* @param {any} fn
|
|
115
|
+
*/
|
|
116
|
+
const isSelfReferencing = (fn) => {
|
|
117
|
+
const names = selfReferenceNames(fn);
|
|
118
|
+
|
|
119
|
+
if (!names.length || !fn.body) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const stack = [fn.body];
|
|
124
|
+
|
|
125
|
+
while (stack.length) {
|
|
126
|
+
const current = stack.pop();
|
|
127
|
+
|
|
128
|
+
if (!current || typeof current !== 'object') continue;
|
|
129
|
+
|
|
130
|
+
if (Array.isArray(current)) {
|
|
131
|
+
stack.push(...current);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (typeof current.type !== 'string') continue;
|
|
136
|
+
|
|
137
|
+
for (const { kind, name } of names) {
|
|
138
|
+
if (kind === 'name' && current.type === 'Identifier' && current.name === name) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (
|
|
143
|
+
kind === 'this' &&
|
|
144
|
+
current.type === 'MemberExpression' &&
|
|
145
|
+
current.object.type === 'ThisExpression' &&
|
|
146
|
+
!current.computed &&
|
|
147
|
+
current.property.type === 'Identifier' &&
|
|
148
|
+
current.property.name === name
|
|
149
|
+
) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
for (const key of Object.keys(current)) {
|
|
155
|
+
if (key === 'parent') continue;
|
|
156
|
+
const value = current[key];
|
|
157
|
+
if (value && typeof value === 'object') stack.push(value);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return false;
|
|
162
|
+
};
|
|
163
|
+
|
|
72
164
|
/**
|
|
73
165
|
* @param {import('eslint').Rule.Node} node
|
|
74
166
|
*/
|
|
@@ -81,6 +173,7 @@ const noTrivialReturnType = {
|
|
|
81
173
|
|
|
82
174
|
const annotation = fn.returnType.typeAnnotation;
|
|
83
175
|
if (!TRIVIAL_TYPES.has(annotation.type)) return;
|
|
176
|
+
if (isSelfReferencing(fn)) return;
|
|
84
177
|
|
|
85
178
|
const label = TRIVIAL_LABELS[annotation.type] ?? annotation.type;
|
|
86
179
|
|