@tuomashatakka/eslint-config 3.1.0 → 3.1.1
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.
|
@@ -2,7 +2,8 @@ name: Publish to npm
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths: ['package.json']
|
|
6
7
|
|
|
7
8
|
jobs:
|
|
8
9
|
publish:
|
|
@@ -12,34 +13,47 @@ jobs:
|
|
|
12
13
|
steps:
|
|
13
14
|
- uses: actions/checkout@v4
|
|
14
15
|
with:
|
|
15
|
-
|
|
16
|
+
fetch-depth: 2
|
|
16
17
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
17
18
|
|
|
19
|
+
- name: Detect version change
|
|
20
|
+
id: version
|
|
21
|
+
run: |
|
|
22
|
+
CURRENT=$(jq -r .version package.json)
|
|
23
|
+
PREVIOUS=$(git show HEAD~1:package.json 2>/dev/null | jq -r .version 2>/dev/null || echo "")
|
|
24
|
+
echo "CURRENT=$CURRENT" >> "$GITHUB_OUTPUT"
|
|
25
|
+
echo "PREVIOUS=$PREVIOUS" >> "$GITHUB_OUTPUT"
|
|
26
|
+
if [ "$CURRENT" != "$PREVIOUS" ] && [ -n "$CURRENT" ]; then
|
|
27
|
+
echo "CHANGED=true" >> "$GITHUB_OUTPUT"
|
|
28
|
+
echo "Version bumped: $PREVIOUS -> $CURRENT"
|
|
29
|
+
else
|
|
30
|
+
echo "CHANGED=false" >> "$GITHUB_OUTPUT"
|
|
31
|
+
echo "No version change ($CURRENT); skipping publish."
|
|
32
|
+
fi
|
|
33
|
+
|
|
18
34
|
- uses: actions/setup-node@v4
|
|
35
|
+
if: steps.version.outputs.CHANGED == 'true'
|
|
19
36
|
with:
|
|
20
37
|
node-version: 22
|
|
21
38
|
registry-url: https://registry.npmjs.org
|
|
22
39
|
cache: npm
|
|
23
40
|
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"
|
|
27
|
-
|
|
28
|
-
- name: Sync package.json version with tag
|
|
29
|
-
run: npm version ${{ steps.version.outputs.VERSION }} --no-git-tag-version
|
|
41
|
+
- if: steps.version.outputs.CHANGED == 'true'
|
|
42
|
+
run: npm ci
|
|
30
43
|
|
|
31
|
-
-
|
|
32
|
-
|
|
44
|
+
- if: steps.version.outputs.CHANGED == 'true'
|
|
45
|
+
run: npm run test
|
|
33
46
|
|
|
34
47
|
- name: Publish to npm
|
|
48
|
+
if: steps.version.outputs.CHANGED == 'true'
|
|
35
49
|
run: npm publish --access public
|
|
36
50
|
env:
|
|
37
51
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
38
52
|
|
|
39
|
-
- name:
|
|
53
|
+
- name: Tag release
|
|
54
|
+
if: steps.version.outputs.CHANGED == 'true'
|
|
40
55
|
run: |
|
|
41
56
|
git config user.name "github-actions[bot]"
|
|
42
57
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
43
|
-
git
|
|
44
|
-
git
|
|
45
|
-
git push origin main
|
|
58
|
+
git tag "v${{ steps.version.outputs.CURRENT }}"
|
|
59
|
+
git push origin "v${{ steps.version.outputs.CURRENT }}"
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @fileoverview Rule to omit unnecessary parentheses, brackets, and braces
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { isValidDotNotationIdentifier, isDeclaration, isParenthesized } from '../utils.mjs'
|
|
5
|
+
import { isValidDotNotationIdentifier, isDeclaration, isParenthesized, statementCanAbsorbElse } from '../utils.mjs'
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
export default {
|
|
@@ -160,13 +160,39 @@ function checkUnnecessaryBraces (blockStatement, controllingNode) {
|
|
|
160
160
|
if (singleStatement.type === 'BlockStatement')
|
|
161
161
|
return
|
|
162
162
|
|
|
163
|
+
// Dangling-else hazard: stripping the braces around an if/else if's
|
|
164
|
+
// consequent could let a trailing `else` rebind to a dangling inner
|
|
165
|
+
// `if`. Skip the fix entirely in that case.
|
|
166
|
+
const isConsequentOfIfWithElse =
|
|
167
|
+
controllingNode &&
|
|
168
|
+
controllingNode.type === 'IfStatement' &&
|
|
169
|
+
controllingNode.consequent === blockStatement &&
|
|
170
|
+
Boolean(controllingNode.alternate)
|
|
171
|
+
|
|
172
|
+
if (isConsequentOfIfWithElse && statementCanAbsorbElse(singleStatement))
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
// ASI hazard: when the next syntactic token after `}` is a keyword
|
|
176
|
+
// (`else` / `while` of a do-while), stripping the braces leaves the
|
|
177
|
+
// inner statement directly followed by that keyword on the same line.
|
|
178
|
+
// For statements that don't end with `;` or `}` in the source (e.g.
|
|
179
|
+
// `return x` inside a block where ASI fired at the closing brace),
|
|
180
|
+
// the result no longer parses. Detect this and either re-inject a `;`
|
|
181
|
+
// or skip the fix.
|
|
182
|
+
const tokenAfterBlock = sourceCode.getTokenAfter(blockStatement)
|
|
183
|
+
const needsAsiGuard =
|
|
184
|
+
tokenAfterBlock &&
|
|
185
|
+
tokenAfterBlock.type === 'Keyword' &&
|
|
186
|
+
(tokenAfterBlock.value === 'else' || tokenAfterBlock.value === 'while') &&
|
|
187
|
+
tokenAfterBlock.loc.start.line === blockStatement.loc.end.line
|
|
188
|
+
|
|
163
189
|
context.report({
|
|
164
190
|
node: blockStatement,
|
|
165
191
|
messageId: 'unnecessaryBraces',
|
|
166
192
|
fix (fixer) {
|
|
167
193
|
const firstToken = sourceCode.getFirstToken(blockStatement)
|
|
168
194
|
const lastToken = sourceCode.getLastToken(blockStatement)
|
|
169
|
-
|
|
195
|
+
let innerText = sourceCode.getText(singleStatement)
|
|
170
196
|
|
|
171
197
|
if (!firstToken || firstToken.value !== '{' || !lastToken || lastToken.value !== '}')
|
|
172
198
|
return null
|
|
@@ -190,6 +216,12 @@ function checkUnnecessaryBraces (blockStatement, controllingNode) {
|
|
|
190
216
|
return null
|
|
191
217
|
}
|
|
192
218
|
|
|
219
|
+
if (needsAsiGuard) {
|
|
220
|
+
const trimmed = innerText.replace(/\s+$/, '')
|
|
221
|
+
if (!trimmed.endsWith(';') && !trimmed.endsWith('}'))
|
|
222
|
+
innerText = `${trimmed};`
|
|
223
|
+
}
|
|
224
|
+
|
|
193
225
|
return fixer.replaceTextRange(blockStatement.range, innerText)
|
|
194
226
|
},
|
|
195
227
|
})
|
package/plugins/omit/utils.mjs
CHANGED
|
@@ -84,8 +84,37 @@ function isParenthesized (node, sourceCode) {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
|
|
87
|
+
// Returns true when `stmt`, if placed where an `else` may follow, would
|
|
88
|
+
// cause the trailing `else` to bind to an inner `if` instead of the outer
|
|
89
|
+
// controlling node (the classic dangling-else hazard).
|
|
90
|
+
export
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
function statementCanAbsorbElse (stmt) {
|
|
94
|
+
if (!stmt)
|
|
95
|
+
return false
|
|
96
|
+
|
|
97
|
+
switch (stmt.type) {
|
|
98
|
+
case 'IfStatement':
|
|
99
|
+
if (!stmt.alternate)
|
|
100
|
+
return true
|
|
101
|
+
return statementCanAbsorbElse(stmt.alternate)
|
|
102
|
+
case 'ForStatement':
|
|
103
|
+
case 'ForInStatement':
|
|
104
|
+
case 'ForOfStatement':
|
|
105
|
+
case 'WhileStatement':
|
|
106
|
+
case 'WithStatement':
|
|
107
|
+
case 'LabeledStatement':
|
|
108
|
+
return statementCanAbsorbElse(stmt.body)
|
|
109
|
+
default:
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
87
115
|
export default {
|
|
88
116
|
isParenthesized,
|
|
89
117
|
isDeclaration,
|
|
90
118
|
isValidDotNotationIdentifier,
|
|
119
|
+
statementCanAbsorbElse,
|
|
91
120
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// expect-warning: omit/omit-unnecessary-parens-brackets
|
|
2
|
+
// Stripping these braces would produce `return 'x' else if (...) return 'red' else return null`
|
|
3
|
+
// which is an ASI parse error. The autofix must inject `;` so the result still parses.
|
|
4
|
+
export function pickColor (x: unknown): string | null {
|
|
5
|
+
if (x === true) { return 'x' } else if (x === 'blue') { return 'red' } else { return null }
|
|
6
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface Material { uniforms: { time: { value: number }}}
|
|
2
|
+
declare function isAnimatedShader (m: Material): boolean
|
|
3
|
+
|
|
4
|
+
type MeshType = { material: Material | Material[] | null | undefined }
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
// Dangling-else hazard: the outer braces are load-bearing — without
|
|
8
|
+
// them, the trailing `else` would re-bind to `if (isAnimatedShader(m))`,
|
|
9
|
+
// silently changing semantics. The omit rule must NOT flag these braces.
|
|
10
|
+
export function update (mesh: MeshType, t: number) {
|
|
11
|
+
if (Array.isArray(mesh.material)) {
|
|
12
|
+
for (const m of mesh.material)
|
|
13
|
+
if (isAnimatedShader(m))
|
|
14
|
+
m.uniforms.time.value = t
|
|
15
|
+
}
|
|
16
|
+
else if (isAnimatedShader(mesh.material!))
|
|
17
|
+
mesh.material!.uniforms.time.value = t
|
|
18
|
+
}
|