@taiga-ui/eslint-plugin-experience-next 0.471.0 → 0.472.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 +15 -7
- package/index.esm.js +93 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ export default [
|
|
|
44
44
|
| decorator-key-sort | Sorts the keys of the object passed to the `@Component/@Injectable/@NgModule/@Pipe` decorator | ✅ | 🔧 | |
|
|
45
45
|
| flat-exports | Spread nested arrays when exporting Angular entity collections | | 🔧 | |
|
|
46
46
|
| host-attributes-sort | Sort Angular host metadata attributes using configurable attribute groups | ✅ | 🔧 | |
|
|
47
|
-
| injection-token-description |
|
|
47
|
+
| injection-token-description | Require `InjectionToken` descriptions to include the token name | ✅ | 🔧 | |
|
|
48
48
|
| no-deep-imports | Disables deep imports of Taiga UI packages | ✅ | 🔧 | |
|
|
49
49
|
| no-deep-imports-to-indexed-packages | Disallow deep imports from packages that expose an index.ts next to ng-package.json or package.json | ✅ | 🔧 | |
|
|
50
50
|
| no-fully-untracked-effect | Disallow reactive callbacks where all signal reads are hidden inside `untracked()` | ✅ | | |
|
|
@@ -282,17 +282,25 @@ Use atomic presets when you want a custom order instead of one of the bundled al
|
|
|
282
282
|
|
|
283
283
|
## injection-token-description
|
|
284
284
|
|
|
285
|
-
<sup>`✅ Recommended`</sup>
|
|
285
|
+
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
286
286
|
|
|
287
|
-
The description
|
|
288
|
-
|
|
287
|
+
The description passed to `new InjectionToken(...)` must contain the name of the variable it is assigned to. The rule
|
|
288
|
+
accepts both direct string descriptions and Angular's `ngDevMode ? '...' : ''` pattern, and the autofix rewrites invalid
|
|
289
|
+
descriptions to the dev-only form. If `ngDevMode` is not declared in the file, the autofix inserts
|
|
290
|
+
`declare const ngDevMode: boolean;` after imports.
|
|
289
291
|
|
|
290
292
|
```ts
|
|
291
|
-
// ❌ error
|
|
292
|
-
|
|
293
|
+
// ❌ error
|
|
294
|
+
import {InjectionToken} from '@angular/core';
|
|
295
|
+
|
|
296
|
+
export const TUI_MY_TOKEN = new InjectionToken<string>('some description');
|
|
293
297
|
|
|
294
298
|
// ✅ after autofix
|
|
295
|
-
|
|
299
|
+
import {InjectionToken} from '@angular/core';
|
|
300
|
+
|
|
301
|
+
declare const ngDevMode: boolean;
|
|
302
|
+
|
|
303
|
+
export const TUI_MY_TOKEN = new InjectionToken<string>(ngDevMode ? '[TUI_MY_TOKEN]: some description' : '');
|
|
296
304
|
```
|
|
297
305
|
|
|
298
306
|
---
|
package/index.esm.js
CHANGED
|
@@ -2024,37 +2024,109 @@ const config$2 = {
|
|
|
2024
2024
|
|
|
2025
2025
|
const MESSAGE_ID$5 = 'invalid-injection-token-description';
|
|
2026
2026
|
const ERROR_MESSAGE$3 = "InjectionToken's description should contain token's name";
|
|
2027
|
+
const NG_DEV_MODE = 'ngDevMode';
|
|
2027
2028
|
const createRule$e = ESLintUtils.RuleCreator((name) => name);
|
|
2029
|
+
function getVariableName(node) {
|
|
2030
|
+
if (node.parent.type !== AST_NODE_TYPES$1.VariableDeclarator) {
|
|
2031
|
+
return undefined;
|
|
2032
|
+
}
|
|
2033
|
+
const { id } = node.parent;
|
|
2034
|
+
return id.type === AST_NODE_TYPES$1.Identifier ? id.name : undefined;
|
|
2035
|
+
}
|
|
2036
|
+
function isStringLiteral$1(node) {
|
|
2037
|
+
return node.type === AST_NODE_TYPES$1.Literal && typeof node.value === 'string';
|
|
2038
|
+
}
|
|
2039
|
+
function isStringLike(node) {
|
|
2040
|
+
return isStringLiteral$1(node) || node.type === AST_NODE_TYPES$1.TemplateLiteral;
|
|
2041
|
+
}
|
|
2042
|
+
function getStringValue(node) {
|
|
2043
|
+
if (isStringLiteral$1(node)) {
|
|
2044
|
+
return node.value;
|
|
2045
|
+
}
|
|
2046
|
+
return node.quasis[0]?.value.raw || '';
|
|
2047
|
+
}
|
|
2048
|
+
function isEmptyString$1(node) {
|
|
2049
|
+
return (getStringValue(node) === '' &&
|
|
2050
|
+
(!('expressions' in node) || !node.expressions.length));
|
|
2051
|
+
}
|
|
2052
|
+
function isNgDevModeConditional(node) {
|
|
2053
|
+
return (node.type === AST_NODE_TYPES$1.ConditionalExpression &&
|
|
2054
|
+
node.test.type === AST_NODE_TYPES$1.Identifier &&
|
|
2055
|
+
node.test.name === NG_DEV_MODE &&
|
|
2056
|
+
isStringLike(node.consequent) &&
|
|
2057
|
+
isStringLike(node.alternate) &&
|
|
2058
|
+
isEmptyString$1(node.alternate));
|
|
2059
|
+
}
|
|
2060
|
+
function getDescriptionValue(node) {
|
|
2061
|
+
if (isStringLike(node)) {
|
|
2062
|
+
return getStringValue(node);
|
|
2063
|
+
}
|
|
2064
|
+
if (isNgDevModeConditional(node)) {
|
|
2065
|
+
return getStringValue(node.consequent);
|
|
2066
|
+
}
|
|
2067
|
+
return undefined;
|
|
2068
|
+
}
|
|
2069
|
+
function getDescriptionNode(node) {
|
|
2070
|
+
if (isStringLike(node)) {
|
|
2071
|
+
return node;
|
|
2072
|
+
}
|
|
2073
|
+
return isNgDevModeConditional(node) ? node.consequent : undefined;
|
|
2074
|
+
}
|
|
2075
|
+
function prependTokenName(text, name) {
|
|
2076
|
+
return `${text.slice(0, 1)}[${name}]: ${text.slice(1)}`;
|
|
2077
|
+
}
|
|
2078
|
+
function isNgDevModeVisible(sourceCode, node) {
|
|
2079
|
+
for (let scope = sourceCode.getScope(node); scope !== null; scope = scope.upper) {
|
|
2080
|
+
if (scope.variables.some((variable) => variable.name === NG_DEV_MODE)) {
|
|
2081
|
+
return true;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
return false;
|
|
2085
|
+
}
|
|
2086
|
+
function getNgDevModeDeclarationFix(program, fixer) {
|
|
2087
|
+
const lastImport = [...program.body]
|
|
2088
|
+
.reverse()
|
|
2089
|
+
.find((statement) => statement.type === AST_NODE_TYPES$1.ImportDeclaration);
|
|
2090
|
+
if (lastImport) {
|
|
2091
|
+
return fixer.insertTextAfter(lastImport, '\n\ndeclare const ngDevMode: boolean;');
|
|
2092
|
+
}
|
|
2093
|
+
const [firstStatement] = program.body;
|
|
2094
|
+
if (firstStatement) {
|
|
2095
|
+
return fixer.insertTextBefore(firstStatement, 'declare const ngDevMode: boolean;\n\n');
|
|
2096
|
+
}
|
|
2097
|
+
return fixer.insertTextBeforeRange([0, 0], 'declare const ngDevMode: boolean;\n');
|
|
2098
|
+
}
|
|
2028
2099
|
const rule$h = createRule$e({
|
|
2029
2100
|
create(context) {
|
|
2101
|
+
const { sourceCode } = context;
|
|
2102
|
+
const program = sourceCode.ast;
|
|
2103
|
+
let shouldAddNgDevModeDeclaration = true;
|
|
2030
2104
|
return {
|
|
2031
2105
|
'NewExpression[callee.name="InjectionToken"]'(node) {
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
const [description] = node?.arguments ?? [];
|
|
2035
|
-
if (!description) {
|
|
2106
|
+
const [description] = node.arguments;
|
|
2107
|
+
if (!description || description.type === AST_NODE_TYPES$1.SpreadElement) {
|
|
2036
2108
|
return;
|
|
2037
2109
|
}
|
|
2038
|
-
const
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
token = description.value;
|
|
2042
|
-
}
|
|
2043
|
-
if (description.type === AST_NODE_TYPES$1.TemplateLiteral) {
|
|
2044
|
-
token = description.quasis[0]?.value.raw || '';
|
|
2045
|
-
}
|
|
2046
|
-
if (node?.parent.type === AST_NODE_TYPES$1.VariableDeclarator) {
|
|
2047
|
-
const id = node.parent.id;
|
|
2048
|
-
if (id.type === AST_NODE_TYPES$1.Identifier) {
|
|
2049
|
-
name = id.name;
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2110
|
+
const name = getVariableName(node);
|
|
2111
|
+
const token = getDescriptionValue(description);
|
|
2112
|
+
const fixedDescription = getDescriptionNode(description);
|
|
2052
2113
|
const report = name && token && !token.includes(name);
|
|
2053
|
-
if (report) {
|
|
2114
|
+
if (report && fixedDescription) {
|
|
2054
2115
|
context.report({
|
|
2055
2116
|
fix: (fixer) => {
|
|
2056
|
-
const
|
|
2057
|
-
|
|
2117
|
+
const isNgDevModeGuarded = isNgDevModeConditional(description);
|
|
2118
|
+
const fixes = [
|
|
2119
|
+
fixer.replaceText(isNgDevModeGuarded ? fixedDescription : description, isNgDevModeGuarded
|
|
2120
|
+
? prependTokenName(sourceCode.getText(fixedDescription), name)
|
|
2121
|
+
: `${NG_DEV_MODE} ? ${prependTokenName(sourceCode.getText(fixedDescription), name)} : ''`),
|
|
2122
|
+
];
|
|
2123
|
+
if (!isNgDevModeGuarded &&
|
|
2124
|
+
shouldAddNgDevModeDeclaration &&
|
|
2125
|
+
!isNgDevModeVisible(sourceCode, description)) {
|
|
2126
|
+
shouldAddNgDevModeDeclaration = false;
|
|
2127
|
+
fixes.unshift(getNgDevModeDeclarationFix(program, fixer));
|
|
2128
|
+
}
|
|
2129
|
+
return fixes;
|
|
2058
2130
|
},
|
|
2059
2131
|
messageId: MESSAGE_ID$5,
|
|
2060
2132
|
node: description,
|