eslint-plugin-prefer-let 4.1.0 → 4.2.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/CHANGELOG.md +4 -0
- package/README.md +32 -0
- package/lib/rules/prefer-let.js +71 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# eslint-plugin-prefer-let
|
|
2
2
|
|
|
3
|
+
## \[4.2.0]
|
|
4
|
+
|
|
5
|
+
- [`9066434`](https://github.com/thefrontside/javascript/commit/90664342144bce7d4a13812e82c155ba3d4ea7e0) Add `forceUpperCaseConst` option
|
|
6
|
+
|
|
3
7
|
## \[4.1.0]
|
|
4
8
|
|
|
5
9
|
- [`6a5ba09`](https://github.com/thefrontside/javascript/commit/6a5ba096b531357d4c9ddd41fe158623cbebf601) Support ESLint v10
|
package/README.md
CHANGED
|
@@ -82,6 +82,38 @@ Then configure the rules you want to use under the rules section.
|
|
|
82
82
|
}
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
### Options
|
|
86
|
+
|
|
87
|
+
#### `forceUpperCaseConst`
|
|
88
|
+
|
|
89
|
+
When set to `true`, this option enforces `const` for top-level `UPPER_CASE` names (e.g. `PI`, `API_BASE_URL`)
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"rules": {
|
|
94
|
+
"prefer-let/prefer-let": [2, { "forceUpperCaseConst": true }]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This makes the distinction between true constants and regular bindings explicit and machine-enforced.
|
|
100
|
+
|
|
101
|
+
Good:
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
const PI = 3.14;
|
|
105
|
+
const API_BASE_URL = 'https://example.com';
|
|
106
|
+
|
|
107
|
+
let config = loadConfig();
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Bad:
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
const config = loadConfig(); // not UPPER_CASE — use let
|
|
114
|
+
let PI = 3.14; // UPPER_CASE — use const
|
|
115
|
+
```
|
|
116
|
+
|
|
85
117
|
### Possible Conflicts
|
|
86
118
|
|
|
87
119
|
This plugin may conflict with other plugins or configs that set `eslint prefer-const`. You can configure the rules to avoid this:
|
package/lib/rules/prefer-let.js
CHANGED
|
@@ -17,12 +17,22 @@ module.exports = {
|
|
|
17
17
|
},
|
|
18
18
|
fixable: "code", // or "code" or "whitespace"
|
|
19
19
|
schema: [
|
|
20
|
-
|
|
20
|
+
{
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
forceUpperCaseConst: {
|
|
24
|
+
type: "boolean"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
additionalProperties: false
|
|
28
|
+
}
|
|
21
29
|
]
|
|
22
30
|
},
|
|
23
31
|
|
|
24
32
|
create: function(context) {
|
|
25
33
|
let sourceCode = context.sourceCode ?? context.getSourceCode();
|
|
34
|
+
let options = context.options[0] || {};
|
|
35
|
+
let forceUpperCaseConst = options.forceUpperCaseConst || false;
|
|
26
36
|
|
|
27
37
|
//----------------------------------------------------------------------
|
|
28
38
|
// Helpers
|
|
@@ -51,6 +61,40 @@ module.exports = {
|
|
|
51
61
|
return isGlobalScope(node) || isModuleScope(node) || isProgramScope(node);
|
|
52
62
|
}
|
|
53
63
|
|
|
64
|
+
function isUpperCase(name) {
|
|
65
|
+
return /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/.test(name);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getBindingNames(node) {
|
|
69
|
+
if (node.type === 'Identifier') {
|
|
70
|
+
return [node.name];
|
|
71
|
+
}
|
|
72
|
+
if (node.type === 'ObjectPattern') {
|
|
73
|
+
return node.properties.flatMap(function(prop) {
|
|
74
|
+
return getBindingNames(prop.value || prop.argument);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (node.type === 'ArrayPattern') {
|
|
78
|
+
return node.elements.filter(Boolean).flatMap(function(el) {
|
|
79
|
+
return getBindingNames(el);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (node.type === 'RestElement') {
|
|
83
|
+
return getBindingNames(node.argument);
|
|
84
|
+
}
|
|
85
|
+
if (node.type === 'AssignmentPattern') {
|
|
86
|
+
return getBindingNames(node.left);
|
|
87
|
+
}
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function allDeclaratorsUpperCase(node) {
|
|
92
|
+
return node.declarations.every(function(decl) {
|
|
93
|
+
let names = getBindingNames(decl.id);
|
|
94
|
+
return names.length > 0 && names.every(isUpperCase);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
54
98
|
function isInAmbientContext(node) {
|
|
55
99
|
let current = node.parent;
|
|
56
100
|
while (current) {
|
|
@@ -76,14 +120,35 @@ module.exports = {
|
|
|
76
120
|
message: 'prefer `let` over `var` to declare value bindings',
|
|
77
121
|
node
|
|
78
122
|
});
|
|
79
|
-
} else if (node.kind === 'const'
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
} else if (node.kind === 'const') {
|
|
124
|
+
if (isTopLevelScope(node)) {
|
|
125
|
+
if (forceUpperCaseConst && !allDeclaratorsUpperCase(node)) {
|
|
126
|
+
let constToken = sourceCode.getFirstToken(node);
|
|
127
|
+
context.report({
|
|
128
|
+
message: '`const` declaration for non-constant names at top-level scope. Use `let` or rename to UPPER_CASE',
|
|
129
|
+
node,
|
|
130
|
+
fix: function(fixer) {
|
|
131
|
+
return fixer.replaceText(constToken, 'let');
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
let constToken = sourceCode.getFirstToken(node);
|
|
137
|
+
context.report({
|
|
138
|
+
message: '`const` declaration outside top-level scope',
|
|
139
|
+
node,
|
|
140
|
+
fix: function(fixer) {
|
|
141
|
+
return fixer.replaceText(constToken, 'let');
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
} else if (node.kind === 'let' && forceUpperCaseConst && isTopLevelScope(node) && allDeclaratorsUpperCase(node)) {
|
|
146
|
+
let letToken = sourceCode.getFirstToken(node);
|
|
82
147
|
context.report({
|
|
83
|
-
message: '`const`
|
|
148
|
+
message: 'use `const` for constant names (UPPER_CASE) at top-level scope',
|
|
84
149
|
node,
|
|
85
150
|
fix: function(fixer) {
|
|
86
|
-
return fixer.replaceText(
|
|
151
|
+
return fixer.replaceText(letToken, 'const');
|
|
87
152
|
}
|
|
88
153
|
});
|
|
89
154
|
}
|