@taiga-ui/eslint-plugin-experience-next 0.527.0 → 0.529.0
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/README.md +1 -0
- package/index.d.ts +3 -0
- package/index.esm.js +164 -46
- package/package.json +1 -1
- package/rules/recommended/class-accessibility-spacing.d.ts +4 -0
- package/rules/recommended/no-implicit-public.d.ts +2 -1
- package/rules/utils/ast/class-members.d.ts +8 -0
- package/rules/utils/ast/spacing.d.ts +1 -0
package/README.md
CHANGED
|
@@ -75,6 +75,7 @@ from third-party plugins. The exact severities and file globs live in
|
|
|
75
75
|
| [array-as-const](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/array-as-const.md) | Exported array of class references should be marked with `as const` | | 🔧 | |
|
|
76
76
|
| [at-compat](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/at-compat.md) | Keep built-in `.at()` and indexed access aligned with the TypeScript target | ✅ | 🔧 | |
|
|
77
77
|
| [attrs-newline](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/attrs-newline.md) | Enforce one attribute per line when a start tag spans multiple lines | ✅ | 🔧 | |
|
|
78
|
+
| [class-accessibility-spacing](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/class-accessibility-spacing.md) | Separate adjacent class members with a blank line when their accessibility group changes | ✅ | 🔧 | |
|
|
78
79
|
| [class-property-naming](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/class-property-naming.md) | Enforce custom naming for class properties based on their type | | 🔧 | |
|
|
79
80
|
| [decorator-key-sort](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/decorator-key-sort.md) | Sorts the keys of the object passed to the `@Component/@Injectable/@NgModule/@Pipe` decorator | ✅ | 🔧 | |
|
|
80
81
|
| [element-newline](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/element-newline.md) | Require line breaks around block-level child nodes in HTML templates | ✅ | 🔧 | |
|
package/index.d.ts
CHANGED
|
@@ -18,6 +18,9 @@ declare const plugin: {
|
|
|
18
18
|
'attrs-newline': import("eslint").Rule.RuleModule & {
|
|
19
19
|
name: string;
|
|
20
20
|
};
|
|
21
|
+
'class-accessibility-spacing': import("@typescript-eslint/utils/ts-eslint").RuleModule<"classAccessibilitySpacing", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
22
|
+
name: string;
|
|
23
|
+
};
|
|
21
24
|
'class-property-naming': import("@typescript-eslint/utils/ts-eslint").RuleModule<"invalidName", [import("./rules/taiga-specific/class-property-naming").RuleConfig[]], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
22
25
|
name: string;
|
|
23
26
|
};
|
package/index.esm.js
CHANGED
|
@@ -493,6 +493,11 @@ function hasBlankLine(text) {
|
|
|
493
493
|
}
|
|
494
494
|
return false;
|
|
495
495
|
}
|
|
496
|
+
function hasBlankLineBetweenNodes(text) {
|
|
497
|
+
const lines = splitLines(text);
|
|
498
|
+
const linesBetweenNodes = lines.slice(1, -1);
|
|
499
|
+
return linesBetweenNodes.some((line) => line.trim() === '');
|
|
500
|
+
}
|
|
496
501
|
function getLineBreak(text) {
|
|
497
502
|
if (text.includes('\r\n')) {
|
|
498
503
|
return '\r\n';
|
|
@@ -1247,6 +1252,7 @@ var recommended = defineConfig([
|
|
|
1247
1252
|
'@angular-eslint/use-lifecycle-interface': 'error',
|
|
1248
1253
|
'@angular-eslint/use-pipe-transform-interface': 'error',
|
|
1249
1254
|
'@taiga-ui/experience-next/at-compat': 'error',
|
|
1255
|
+
'@taiga-ui/experience-next/class-accessibility-spacing': 'error',
|
|
1250
1256
|
'@taiga-ui/experience-next/decorator-key-sort': [
|
|
1251
1257
|
'error',
|
|
1252
1258
|
{
|
|
@@ -14974,6 +14980,40 @@ function getParenthesizedExpression(expression) {
|
|
|
14974
14980
|
: null;
|
|
14975
14981
|
}
|
|
14976
14982
|
|
|
14983
|
+
function isFieldLikeMember(member) {
|
|
14984
|
+
return (member.type === dist$4.AST_NODE_TYPES.PropertyDefinition ||
|
|
14985
|
+
member.type === dist$4.AST_NODE_TYPES.TSAbstractPropertyDefinition);
|
|
14986
|
+
}
|
|
14987
|
+
function isAccessorMember(member) {
|
|
14988
|
+
return (member.type === dist$4.AST_NODE_TYPES.MethodDefinition &&
|
|
14989
|
+
(member.kind === 'get' || member.kind === 'set'));
|
|
14990
|
+
}
|
|
14991
|
+
function isRelevantSpacingClassMember(member) {
|
|
14992
|
+
return isFieldLikeMember(member) || isAccessorMember(member);
|
|
14993
|
+
}
|
|
14994
|
+
function isAccessibilityClassMember(member) {
|
|
14995
|
+
switch (member.type) {
|
|
14996
|
+
case dist$4.AST_NODE_TYPES.AccessorProperty:
|
|
14997
|
+
case dist$4.AST_NODE_TYPES.MethodDefinition:
|
|
14998
|
+
case dist$4.AST_NODE_TYPES.PropertyDefinition:
|
|
14999
|
+
case dist$4.AST_NODE_TYPES.TSAbstractAccessorProperty:
|
|
15000
|
+
case dist$4.AST_NODE_TYPES.TSAbstractMethodDefinition:
|
|
15001
|
+
case dist$4.AST_NODE_TYPES.TSAbstractPropertyDefinition:
|
|
15002
|
+
case dist$4.AST_NODE_TYPES.TSIndexSignature:
|
|
15003
|
+
return true;
|
|
15004
|
+
default:
|
|
15005
|
+
return false;
|
|
15006
|
+
}
|
|
15007
|
+
}
|
|
15008
|
+
function isEcmascriptPrivateClassMember(member) {
|
|
15009
|
+
return 'key' in member && member.key.type === dist$4.AST_NODE_TYPES.PrivateIdentifier;
|
|
15010
|
+
}
|
|
15011
|
+
function getAccessibilityGroup(member) {
|
|
15012
|
+
return isEcmascriptPrivateClassMember(member)
|
|
15013
|
+
? 'private'
|
|
15014
|
+
: (member.accessibility ?? 'public');
|
|
15015
|
+
}
|
|
15016
|
+
|
|
14977
15017
|
const EQUALITY_OPERATORS = new Set(['!=', '!==', '==', '===']);
|
|
14978
15018
|
function getChainExpressionRoot(node) {
|
|
14979
15019
|
const parent = getParentNode(node);
|
|
@@ -15189,7 +15229,7 @@ function getMemberExpressionPropertyName(node) {
|
|
|
15189
15229
|
return node.computed ? getStaticStringValue(node.property) : null;
|
|
15190
15230
|
}
|
|
15191
15231
|
function getClassMemberName(member) {
|
|
15192
|
-
return member
|
|
15232
|
+
return isEcmascriptPrivateClassMember(member)
|
|
15193
15233
|
? null
|
|
15194
15234
|
: getStaticPropertyName(member.key);
|
|
15195
15235
|
}
|
|
@@ -216112,12 +216152,10 @@ function getPreferredTemporaryNames(node) {
|
|
|
216112
216152
|
];
|
|
216113
216153
|
}
|
|
216114
216154
|
function getClassElementName(member) {
|
|
216115
|
-
if (
|
|
216116
|
-
return
|
|
216155
|
+
if (isEcmascriptPrivateClassMember(member)) {
|
|
216156
|
+
return member.key.name;
|
|
216117
216157
|
}
|
|
216118
|
-
return member.key
|
|
216119
|
-
? member.key.name
|
|
216120
|
-
: getStaticPropertyName(member.key);
|
|
216158
|
+
return 'key' in member ? getStaticPropertyName(member.key) : null;
|
|
216121
216159
|
}
|
|
216122
216160
|
function hasClassElementName(classBody, name) {
|
|
216123
216161
|
return classBody.body.some((member) => getClassElementName(member) === name);
|
|
@@ -216267,7 +216305,7 @@ function getLastIndexFix(fixer, sourceCode, node, call) {
|
|
|
216267
216305
|
}
|
|
216268
216306
|
return null;
|
|
216269
216307
|
}
|
|
216270
|
-
const rule$
|
|
216308
|
+
const rule$Y = createRule({
|
|
216271
216309
|
create(context) {
|
|
216272
216310
|
const typeAwareContext = getTypeAwareRuleContext(context);
|
|
216273
216311
|
const compilerOptions = typeAwareContext.tsProgram.getCompilerOptions();
|
|
@@ -248073,7 +248111,7 @@ function buildMultilineStartTag(node, sourceText) {
|
|
|
248073
248111
|
closing,
|
|
248074
248112
|
].join(lineBreak);
|
|
248075
248113
|
}
|
|
248076
|
-
const rule$
|
|
248114
|
+
const rule$X = createRule({
|
|
248077
248115
|
name: 'attrs-newline',
|
|
248078
248116
|
rule: {
|
|
248079
248117
|
create(context) {
|
|
@@ -248159,6 +248197,59 @@ const rule$W = createRule({
|
|
|
248159
248197
|
},
|
|
248160
248198
|
});
|
|
248161
248199
|
|
|
248200
|
+
const rule$W = createRule({
|
|
248201
|
+
create(context) {
|
|
248202
|
+
const sourceCode = context.sourceCode;
|
|
248203
|
+
return {
|
|
248204
|
+
ClassBody(node) {
|
|
248205
|
+
for (let index = 0; index < node.body.length - 1; index++) {
|
|
248206
|
+
const current = node.body[index];
|
|
248207
|
+
const next = node.body[index + 1];
|
|
248208
|
+
if (!current ||
|
|
248209
|
+
!next ||
|
|
248210
|
+
!isAccessibilityClassMember(current) ||
|
|
248211
|
+
!isAccessibilityClassMember(next)) {
|
|
248212
|
+
continue;
|
|
248213
|
+
}
|
|
248214
|
+
const accessibilityGroupChanged = getAccessibilityGroup(current) !== getAccessibilityGroup(next);
|
|
248215
|
+
if (!accessibilityGroupChanged) {
|
|
248216
|
+
continue;
|
|
248217
|
+
}
|
|
248218
|
+
const betweenText = sourceCode.text.slice(current.range[1], next.range[0]);
|
|
248219
|
+
const missingBlankLine = !hasBlankLineBetweenNodes(betweenText);
|
|
248220
|
+
if (!missingBlankLine) {
|
|
248221
|
+
continue;
|
|
248222
|
+
}
|
|
248223
|
+
const reportTarget = {
|
|
248224
|
+
messageId: 'classAccessibilitySpacing',
|
|
248225
|
+
node: next,
|
|
248226
|
+
};
|
|
248227
|
+
if (hasCommentLikeText(betweenText)) {
|
|
248228
|
+
context.report(reportTarget);
|
|
248229
|
+
continue;
|
|
248230
|
+
}
|
|
248231
|
+
context.report({
|
|
248232
|
+
...reportTarget,
|
|
248233
|
+
fix: (fixer) => fixer.replaceTextRange([current.range[1], next.range[0]], getSpacingReplacement(sourceCode, betweenText, next.loc.start.line, 1)),
|
|
248234
|
+
});
|
|
248235
|
+
}
|
|
248236
|
+
},
|
|
248237
|
+
};
|
|
248238
|
+
},
|
|
248239
|
+
meta: {
|
|
248240
|
+
docs: {
|
|
248241
|
+
description: 'Require a blank line between adjacent class members when their accessibility group changes',
|
|
248242
|
+
},
|
|
248243
|
+
fixable: 'code',
|
|
248244
|
+
messages: {
|
|
248245
|
+
classAccessibilitySpacing: 'Class members with different accessibility groups should be separated by a blank line',
|
|
248246
|
+
},
|
|
248247
|
+
schema: [],
|
|
248248
|
+
type: 'layout',
|
|
248249
|
+
},
|
|
248250
|
+
name: 'class-accessibility-spacing',
|
|
248251
|
+
});
|
|
248252
|
+
|
|
248162
248253
|
function isObject(node) {
|
|
248163
248254
|
return node?.type === dist$3.AST_NODE_TYPES.ObjectExpression;
|
|
248164
248255
|
}
|
|
@@ -251223,40 +251314,78 @@ const rule$H = createRule({
|
|
|
251223
251314
|
rule: config$3,
|
|
251224
251315
|
});
|
|
251225
251316
|
|
|
251317
|
+
function isClassMemberCandidate(node) {
|
|
251318
|
+
return (node.type === dist$4.AST_NODE_TYPES.MethodDefinition ||
|
|
251319
|
+
node.type === dist$4.AST_NODE_TYPES.PropertyDefinition);
|
|
251320
|
+
}
|
|
251321
|
+
function isConstructorMember(node) {
|
|
251322
|
+
return node.type === dist$4.AST_NODE_TYPES.MethodDefinition && node.kind === 'constructor';
|
|
251323
|
+
}
|
|
251324
|
+
function getParameterPropertyName(node) {
|
|
251325
|
+
const { parameter } = node;
|
|
251326
|
+
return parameter.type === dist$4.AST_NODE_TYPES.Identifier
|
|
251327
|
+
? parameter.name
|
|
251328
|
+
: parameter.left.name;
|
|
251329
|
+
}
|
|
251330
|
+
function getCandidateName(node) {
|
|
251331
|
+
return node.type === dist$4.AST_NODE_TYPES.TSParameterProperty
|
|
251332
|
+
? (getParameterPropertyName(node) ?? 'member')
|
|
251333
|
+
: (getStaticPropertyName(node.key) ?? 'member');
|
|
251334
|
+
}
|
|
251335
|
+
function getCandidateKind(node) {
|
|
251336
|
+
return node.type === dist$4.AST_NODE_TYPES.MethodDefinition ? node.kind : 'property';
|
|
251337
|
+
}
|
|
251338
|
+
function getFirstNonWhitespaceOffset(text, offset) {
|
|
251339
|
+
let index = offset;
|
|
251340
|
+
while (index < text.length && text[index]?.trim() === '') {
|
|
251341
|
+
index++;
|
|
251342
|
+
}
|
|
251343
|
+
return index;
|
|
251344
|
+
}
|
|
251345
|
+
function getPublicModifierInsertion(options) {
|
|
251346
|
+
const { node, sourceCode } = options;
|
|
251347
|
+
const lastDecoratorIndex = node.decorators.length - 1;
|
|
251348
|
+
const lastDecorator = node.decorators[lastDecoratorIndex];
|
|
251349
|
+
if (lastDecorator) {
|
|
251350
|
+
const [, end] = lastDecorator.range;
|
|
251351
|
+
const memberStart = getFirstNonWhitespaceOffset(sourceCode.text, end);
|
|
251352
|
+
const hasWhitespaceAfterDecorator = memberStart > end;
|
|
251353
|
+
return {
|
|
251354
|
+
range: [memberStart, memberStart],
|
|
251355
|
+
text: hasWhitespaceAfterDecorator ? 'public ' : ' public ',
|
|
251356
|
+
};
|
|
251357
|
+
}
|
|
251358
|
+
return {
|
|
251359
|
+
range: [node.range[0], node.range[0]],
|
|
251360
|
+
text: 'public ',
|
|
251361
|
+
};
|
|
251362
|
+
}
|
|
251226
251363
|
const rule$G = createRule({
|
|
251227
251364
|
create(context) {
|
|
251365
|
+
const sourceCode = context.sourceCode;
|
|
251228
251366
|
const checkImplicitPublic = (node) => {
|
|
251229
251367
|
const classRef = getEnclosingClass(node);
|
|
251230
|
-
if (!classRef
|
|
251231
|
-
node.kind === 'constructor' ||
|
|
251232
|
-
!!node?.accessibility ||
|
|
251233
|
-
node.key?.type === dist$4.AST_NODE_TYPES.PrivateIdentifier) {
|
|
251368
|
+
if (!classRef) {
|
|
251234
251369
|
return;
|
|
251235
251370
|
}
|
|
251236
|
-
const
|
|
251237
|
-
|
|
251238
|
-
(node
|
|
251239
|
-
|
|
251240
|
-
|
|
251241
|
-
node?.range ?? [0, 0];
|
|
251242
|
-
if (node.kind === 'set' || node.kind === 'get') {
|
|
251243
|
-
const [start, end] = node.key.range;
|
|
251244
|
-
range = [start - node.kind.length - 1, end - node.kind.length - 1];
|
|
251245
|
-
}
|
|
251246
|
-
else if (node.kind === 'method' && node.key?.object?.name === 'Symbol') {
|
|
251247
|
-
const [start, end] = range;
|
|
251248
|
-
range = [start - 1, end - 1];
|
|
251249
|
-
}
|
|
251250
|
-
if (node.type === 'PropertyDefinition' && node.decorators?.length > 0) {
|
|
251251
|
-
const [, end] = node.decorators[node.decorators.length - 1].range ?? [];
|
|
251252
|
-
range = [end + 1, end + 2];
|
|
251371
|
+
const privateNameCannotBePublic = isClassMemberCandidate(node) && isEcmascriptPrivateClassMember(node);
|
|
251372
|
+
if (isConstructorMember(node) ||
|
|
251373
|
+
Boolean(node.accessibility) ||
|
|
251374
|
+
privateNameCannotBePublic) {
|
|
251375
|
+
return;
|
|
251253
251376
|
}
|
|
251254
251377
|
context.report({
|
|
251255
251378
|
data: {
|
|
251256
|
-
kind: node
|
|
251257
|
-
name,
|
|
251379
|
+
kind: getCandidateKind(node),
|
|
251380
|
+
name: getCandidateName(node),
|
|
251381
|
+
},
|
|
251382
|
+
fix: (fixer) => {
|
|
251383
|
+
const { range, text } = getPublicModifierInsertion({
|
|
251384
|
+
node,
|
|
251385
|
+
sourceCode,
|
|
251386
|
+
});
|
|
251387
|
+
return fixer.insertTextBeforeRange(range, text);
|
|
251258
251388
|
},
|
|
251259
|
-
fix: (fixer) => fixer.insertTextBeforeRange(range, ' public '),
|
|
251260
251389
|
messageId: 'implicitPublic',
|
|
251261
251390
|
node,
|
|
251262
251391
|
});
|
|
@@ -255580,18 +255709,6 @@ const rule$a = createRule({
|
|
|
255580
255709
|
name: 'short-tui-imports',
|
|
255581
255710
|
});
|
|
255582
255711
|
|
|
255583
|
-
function isFieldLikeMember(member) {
|
|
255584
|
-
return (member.type === dist$4.AST_NODE_TYPES.PropertyDefinition ||
|
|
255585
|
-
member.type === dist$4.AST_NODE_TYPES.TSAbstractPropertyDefinition);
|
|
255586
|
-
}
|
|
255587
|
-
function isAccessorMember(member) {
|
|
255588
|
-
return (member.type === dist$4.AST_NODE_TYPES.MethodDefinition &&
|
|
255589
|
-
(member.kind === 'get' || member.kind === 'set'));
|
|
255590
|
-
}
|
|
255591
|
-
function isRelevantSpacingClassMember(member) {
|
|
255592
|
-
return isFieldLikeMember(member) || isAccessorMember(member);
|
|
255593
|
-
}
|
|
255594
|
-
|
|
255595
255712
|
const rule$9 = createRule({
|
|
255596
255713
|
create(context) {
|
|
255597
255714
|
const sourceCode = context.sourceCode;
|
|
@@ -256958,8 +257075,9 @@ const plugin = {
|
|
|
256958
257075
|
},
|
|
256959
257076
|
rules: {
|
|
256960
257077
|
'array-as-const': rule$5,
|
|
256961
|
-
'at-compat': rule$
|
|
256962
|
-
'attrs-newline': rule$
|
|
257078
|
+
'at-compat': rule$Y,
|
|
257079
|
+
'attrs-newline': rule$X,
|
|
257080
|
+
'class-accessibility-spacing': rule$W,
|
|
256963
257081
|
'class-property-naming': rule$4,
|
|
256964
257082
|
'decorator-key-sort': rule$V,
|
|
256965
257083
|
'element-newline': rule$U,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taiga-ui/eslint-plugin-experience-next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.529.0",
|
|
4
4
|
"description": "An ESLint plugin to enforce a consistent code styles across taiga-ui projects",
|
|
5
5
|
"homepage": "https://github.com/taiga-family/toolkit#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
export declare const rule: TSESLint.RuleModule<"implicitPublic", readonly unknown[], unknown, TSESLint.RuleListener> & {
|
|
2
3
|
name: string;
|
|
3
4
|
};
|
|
4
5
|
export default rule;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { type TSESTree } from '@typescript-eslint/utils';
|
|
2
2
|
export { getLeadingIndentation, getLineBreak, getLineStartOffset, hasBlankLine, hasCommentLikeText, isSingleLineNode, } from './spacing';
|
|
3
3
|
export type FieldLikeMember = TSESTree.PropertyDefinition | TSESTree.TSAbstractPropertyDefinition;
|
|
4
|
+
export type AccessibilityGroup = 'private' | 'protected' | 'public';
|
|
5
|
+
export type AccessibilityClassMember = TSESTree.AccessorProperty | TSESTree.MethodDefinition | TSESTree.PropertyDefinition | TSESTree.TSAbstractAccessorProperty | TSESTree.TSAbstractMethodDefinition | TSESTree.TSAbstractPropertyDefinition | TSESTree.TSIndexSignature;
|
|
6
|
+
export type EcmascriptPrivateClassMember = Exclude<AccessibilityClassMember, TSESTree.TSIndexSignature> & {
|
|
7
|
+
readonly key: TSESTree.PrivateIdentifier;
|
|
8
|
+
};
|
|
4
9
|
export declare function isFieldLikeMember(member: TSESTree.ClassElement): member is FieldLikeMember;
|
|
5
10
|
export declare function isAccessorMember(member: TSESTree.ClassElement): member is TSESTree.MethodDefinition;
|
|
6
11
|
export declare function isRelevantSpacingClassMember(member: TSESTree.ClassElement): member is FieldLikeMember | TSESTree.MethodDefinition;
|
|
12
|
+
export declare function isAccessibilityClassMember(member: TSESTree.ClassElement): member is AccessibilityClassMember;
|
|
13
|
+
export declare function isEcmascriptPrivateClassMember(member: TSESTree.ClassElement): member is EcmascriptPrivateClassMember;
|
|
14
|
+
export declare function getAccessibilityGroup(member: AccessibilityClassMember): AccessibilityGroup;
|
|
@@ -3,6 +3,7 @@ export declare function isSingleLineNode(node: TSESTree.Node): boolean;
|
|
|
3
3
|
export declare function isLineBreakCharacter(char: string | undefined): boolean;
|
|
4
4
|
export declare function hasCommentLikeText(text: string): boolean;
|
|
5
5
|
export declare function hasBlankLine(text: string): boolean;
|
|
6
|
+
export declare function hasBlankLineBetweenNodes(text: string): boolean;
|
|
6
7
|
export declare function getLineBreak(text: string): string;
|
|
7
8
|
export declare function hasLineBreak(text: string): boolean;
|
|
8
9
|
export declare function getLeadingIndentation(text: string): string;
|