esupgrade 2025.15.0 → 2025.16.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/.github/agents/superjoe.agent.md +66 -0
- package/.github/workflows/ci.yml +1 -1
- package/.pre-commit-config.yaml +2 -1
- package/README.md +12 -0
- package/package.json +1 -1
- package/src/types.js +51 -0
- package/src/widelyAvailable/arrayFilterToFind.js +80 -0
- package/src/widelyAvailable.js +1 -0
- package/tests/widelyAvailable/array-filter-to-find.test.js +173 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
# For format details, see: https://gh.io/customagents/config
|
|
3
|
+
|
|
4
|
+
name: SuperJoe
|
|
5
|
+
description: CodingJoe's digital clone following his coding guidelines and best practices.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# SuperJoe
|
|
10
|
+
|
|
11
|
+
## Planning
|
|
12
|
+
|
|
13
|
+
You MUST ALWAYS follow the `naming-things` guidelines. Use the following command to access the guidelines:
|
|
14
|
+
```console
|
|
15
|
+
curl -sSL https://raw.githubusercontent.com/codingjoe/naming-things/refs/heads/main/README.md | cat
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
You MUST ALWAYS read the `CONTRIBUTING.md` file before planning or writing any code.
|
|
19
|
+
You MUST ALWAYS search the documentation and amend or update it as necessary.
|
|
20
|
+
You MUST ALWAYS check for pre-commit hooks and run them before committing code.
|
|
21
|
+
You MUST ALWAYS ensure that all new code is fully tested with 100% coverage. Unreachable code branches MUST be removed.
|
|
22
|
+
|
|
23
|
+
## Writing Code
|
|
24
|
+
|
|
25
|
+
Less code is more! Use the latest language features and libraries to achieve more with less code.
|
|
26
|
+
|
|
27
|
+
Do not add new dependencies, but if you do, they must be widely adopted and well-maintained in the open-source community.
|
|
28
|
+
|
|
29
|
+
You are a strong FOSS advocate with a preference for permissive licenses like BSD or MIT.
|
|
30
|
+
|
|
31
|
+
Use generators instead of adding items to lists or arrays.
|
|
32
|
+
|
|
33
|
+
Use class syntax for all object-oriented code.
|
|
34
|
+
Use named functions instead of anonymous functions whenever possible.
|
|
35
|
+
Avoid overly complex functions. Break them into smaller functions if necessary.
|
|
36
|
+
Docstrings should be written in present tense imperative mood.
|
|
37
|
+
They must start with a capital letter and end with a period.
|
|
38
|
+
Docstrings must describe the external behavior of the function, class, or method.
|
|
39
|
+
Docstrings should avoid redundant phrases like "This function" or "This method".
|
|
40
|
+
Class docstrings must not repeat the class name or start with a verb since they don't do anything themselves.
|
|
41
|
+
Avoid code comments unless they describe behavior of 3rd party code or complex algorithms.
|
|
42
|
+
Avoid loops in favor of recursive functions or generator functions.
|
|
43
|
+
Avoid functions or other code inside functions.
|
|
44
|
+
Avoid if-statements in favor of switch/match-statements or polymorphism.
|
|
45
|
+
Do not assign names to objects which are returned in the next line.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Python
|
|
49
|
+
|
|
50
|
+
Follow PEP 8 guidelines for code style.
|
|
51
|
+
EAFP (Easier to Ask Forgiveness than Permission) is preferred over LBYL (Look Before You Leap).
|
|
52
|
+
Use type hints for all public functions, classes, and methods.
|
|
53
|
+
Use dataclasses for simple data structures.
|
|
54
|
+
Use context managers for resource management.
|
|
55
|
+
Use list/set/dict comprehensions instead of loops for creating collections.
|
|
56
|
+
Use generators for large data sets to save memory.
|
|
57
|
+
Use the walrus operator (`:=`) for inline assignments when it improves readability.
|
|
58
|
+
|
|
59
|
+
### JavaScript
|
|
60
|
+
|
|
61
|
+
Use `#` for private methods.
|
|
62
|
+
Write docstrings with jsdoc type annotations for all functions, classes, and methods.
|
|
63
|
+
|
|
64
|
+
### TypeScript
|
|
65
|
+
|
|
66
|
+
Use `#` for private methods.
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -28,7 +28,7 @@ jobs:
|
|
|
28
28
|
node-version-file: package.json
|
|
29
29
|
- run: npm ci
|
|
30
30
|
- run: node --test --experimental-test-coverage --test-reporter=spec --test-reporter=lcov --test-reporter-destination=stdout --test-reporter-destination=lcov.info
|
|
31
|
-
- uses: codecov/codecov-action@
|
|
31
|
+
- uses: codecov/codecov-action@v6
|
|
32
32
|
with:
|
|
33
33
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
34
34
|
flags: javascript
|
package/.pre-commit-config.yaml
CHANGED
|
@@ -21,6 +21,7 @@ repos:
|
|
|
21
21
|
- mdformat-footnote
|
|
22
22
|
- mdformat-gfm
|
|
23
23
|
- mdformat-gfm-alerts
|
|
24
|
+
exclude: '.github/agents/'
|
|
24
25
|
- repo: https://github.com/google/yamlfmt
|
|
25
26
|
rev: v0.21.0
|
|
26
27
|
hooks:
|
|
@@ -31,7 +32,7 @@ repos:
|
|
|
31
32
|
- id: write-good
|
|
32
33
|
args: [--no-passive]
|
|
33
34
|
- repo: https://github.com/pre-commit/mirrors-eslint
|
|
34
|
-
rev: v10.
|
|
35
|
+
rev: v10.3.0
|
|
35
36
|
hooks:
|
|
36
37
|
- id: eslint
|
|
37
38
|
args: ["--fix"]
|
package/README.md
CHANGED
|
@@ -224,6 +224,17 @@ Supports:
|
|
|
224
224
|
+const clone = [...Array.from(items)];
|
|
225
225
|
```
|
|
226
226
|
|
|
227
|
+
#### `Array.filter()[0]` → [`Array.find()`][mdn-find]
|
|
228
|
+
|
|
229
|
+
```diff
|
|
230
|
+
-const first = [1, 2, 3].filter(n => n > 1)[0];
|
|
231
|
+
+const first = [1, 2, 3].find(n => n > 1);
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Transforms `filter(predicate)[0]` to the more explicit and performant `find(predicate)`, which stops at the first match instead of filtering the entire array.
|
|
235
|
+
|
|
236
|
+
Transformations are limited to when the receiver can be verified as an array (array literals, `new Array()`, or known array method chains) and `filter()` is called with exactly one argument.
|
|
237
|
+
|
|
227
238
|
#### `Math.pow()` → [Exponentiation operator \*\*][mdn-exponentiation]
|
|
228
239
|
|
|
229
240
|
```diff
|
|
@@ -785,6 +796,7 @@ Furthermore, esupgrade supports JavaScript, TypeScript, and more, while lebab is
|
|
|
785
796
|
[mdn-default-parameters]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters
|
|
786
797
|
[mdn-endswith]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
|
|
787
798
|
[mdn-exponentiation]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Exponentiation
|
|
799
|
+
[mdn-find]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
|
|
788
800
|
[mdn-for-of]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
|
|
789
801
|
[mdn-functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
|
|
790
802
|
[mdn-globalthis]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis
|
package/package.json
CHANGED
package/src/types.js
CHANGED
|
@@ -494,6 +494,57 @@ export class NodeTest {
|
|
|
494
494
|
return count
|
|
495
495
|
}
|
|
496
496
|
|
|
497
|
+
/**
|
|
498
|
+
* Determine whether an inline function has side effects.
|
|
499
|
+
* Extracts parameter names from the function node and checks the body against a
|
|
500
|
+
* strict whitelist: only literals, parameter identifiers, binary expressions, and
|
|
501
|
+
* non-computed member access on parameters are side-effect free.
|
|
502
|
+
*
|
|
503
|
+
* @returns {boolean} True if the function has side effects
|
|
504
|
+
*/
|
|
505
|
+
hasSideEffects() {
|
|
506
|
+
const paramNames = new Set(
|
|
507
|
+
this.node.params.flatMap((param) =>
|
|
508
|
+
Array.from(new NodeTest(param).extractIdentifiersFromPattern()),
|
|
509
|
+
),
|
|
510
|
+
)
|
|
511
|
+
return this.#hasNodeSideEffects(this.node.body, paramNames)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Recursively determine if an AST node has side effects given a set of allowed
|
|
516
|
+
* identifiers. Returns false only for literals, known-parameter identifiers, binary
|
|
517
|
+
* expressions, non-computed property access on side-effect-free sub-expressions, and
|
|
518
|
+
* single-return-statement block bodies. Returns true for all other node types.
|
|
519
|
+
*
|
|
520
|
+
* @param {import("ast-types").ASTNode} node - The node to inspect
|
|
521
|
+
* @param {Set<string>} paramNames - Set of allowed identifier names
|
|
522
|
+
* @returns {boolean} True if the node has side effects
|
|
523
|
+
*/
|
|
524
|
+
#hasNodeSideEffects(node, paramNames) {
|
|
525
|
+
if (j.Literal.check(node)) return false
|
|
526
|
+
if (j.Identifier.check(node)) return !paramNames.has(node.name)
|
|
527
|
+
if (j.BinaryExpression.check(node)) {
|
|
528
|
+
return (
|
|
529
|
+
this.#hasNodeSideEffects(node.left, paramNames) ||
|
|
530
|
+
this.#hasNodeSideEffects(node.right, paramNames)
|
|
531
|
+
)
|
|
532
|
+
}
|
|
533
|
+
if (j.MemberExpression.check(node)) {
|
|
534
|
+
if (node.computed) return true
|
|
535
|
+
return this.#hasNodeSideEffects(node.object, paramNames)
|
|
536
|
+
}
|
|
537
|
+
if (j.BlockStatement.check(node)) {
|
|
538
|
+
if (node.body.length !== 1) return true
|
|
539
|
+
const [stmt] = node.body
|
|
540
|
+
return (
|
|
541
|
+
!j.ReturnStatement.check(stmt) ||
|
|
542
|
+
this.#hasNodeSideEffects(stmt.argument, paramNames)
|
|
543
|
+
)
|
|
544
|
+
}
|
|
545
|
+
return true
|
|
546
|
+
}
|
|
547
|
+
|
|
497
548
|
/**
|
|
498
549
|
* Check if node eventually chains from document.
|
|
499
550
|
*
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { default as j } from "jscodeshift"
|
|
2
|
+
import { NodeTest } from "../types.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transform Array.filter()[0] to Array.find().
|
|
6
|
+
* Converts patterns like arr.filter(predicate)[0] to arr.find(predicate).
|
|
7
|
+
*
|
|
8
|
+
* @param {import("jscodeshift").Collection} root - The root AST collection
|
|
9
|
+
* @returns {boolean} True if code was modified
|
|
10
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
|
|
11
|
+
*/
|
|
12
|
+
export function arrayFilterToFind(root) {
|
|
13
|
+
let modified = false
|
|
14
|
+
|
|
15
|
+
root
|
|
16
|
+
.find(j.MemberExpression)
|
|
17
|
+
.filter((path) => {
|
|
18
|
+
const node = path.node
|
|
19
|
+
|
|
20
|
+
// Must be computed access: expr[0]
|
|
21
|
+
if (!node.computed) {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Property must be literal 0
|
|
26
|
+
if (!j.Literal.check(node.property) || node.property.value !== 0) {
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Object must be a .filter() call
|
|
31
|
+
if (!j.CallExpression.check(node.object)) {
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const filterCall = node.object
|
|
36
|
+
|
|
37
|
+
if (
|
|
38
|
+
!j.MemberExpression.check(filterCall.callee) ||
|
|
39
|
+
filterCall.callee.property.name !== "filter"
|
|
40
|
+
) {
|
|
41
|
+
return false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// .filter() must have exactly one argument
|
|
45
|
+
if (filterCall.arguments.length !== 1) {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Predicate must be an inline function whose body is proven side-effect free.
|
|
50
|
+
// Named function references are skipped because their bodies cannot be inspected.
|
|
51
|
+
const predicate = filterCall.arguments[0]
|
|
52
|
+
if (
|
|
53
|
+
!j.ArrowFunctionExpression.check(predicate) &&
|
|
54
|
+
!j.FunctionExpression.check(predicate)
|
|
55
|
+
) {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (new NodeTest(predicate).hasSideEffects()) {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Object being filtered must be a known array
|
|
64
|
+
return new NodeTest(filterCall.callee.object).hasIndexOfAndIncludes()
|
|
65
|
+
})
|
|
66
|
+
.forEach((path) => {
|
|
67
|
+
const filterCall = path.node.object
|
|
68
|
+
|
|
69
|
+
j(path).replaceWith(
|
|
70
|
+
j.callExpression(
|
|
71
|
+
j.memberExpression(filterCall.callee.object, j.identifier("find"), false),
|
|
72
|
+
filterCall.arguments,
|
|
73
|
+
),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
modified = true
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
return modified
|
|
80
|
+
}
|
package/src/widelyAvailable.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { anonymousFunctionToArrow } from "./widelyAvailable/anonymousFunctionToArrow.js"
|
|
2
2
|
export { argumentsToRestParameters } from "./widelyAvailable/argumentsToRestParameters.js"
|
|
3
3
|
export { arrayConcatToSpread } from "./widelyAvailable/arrayConcatToSpread.js"
|
|
4
|
+
export { arrayFilterToFind } from "./widelyAvailable/arrayFilterToFind.js"
|
|
4
5
|
export { arrayFromForEachToForOf } from "./widelyAvailable/arrayFromForEachToForOf.js"
|
|
5
6
|
export { arrayFromToSpread } from "./widelyAvailable/arrayFromToSpread.js"
|
|
6
7
|
export { arraySliceToSpread } from "./widelyAvailable/arraySliceToSpread.js"
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import assert from "node:assert/strict"
|
|
2
|
+
import { describe, suite, test } from "node:test"
|
|
3
|
+
import { transform } from "../../src/index.js"
|
|
4
|
+
|
|
5
|
+
suite("widely-available", () => {
|
|
6
|
+
describe("arrayFilterToFind", () => {
|
|
7
|
+
test("array literal with named function - should not transform", () => {
|
|
8
|
+
const result = transform(`const first = [1, 2, 3].filter(isPositive)[0];`)
|
|
9
|
+
|
|
10
|
+
assert(!result.modified, "skip filter()[0] with named function predicate")
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test("array literal with side effect exception - should not transform", () => {
|
|
14
|
+
const result = transform(`const first = [1, 2, 3].filter(x => {
|
|
15
|
+
throw new Error("Side effect");
|
|
16
|
+
})[0];`)
|
|
17
|
+
|
|
18
|
+
assert(!result.modified, "skip filter()[0] with named function predicate")
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test("array literal with side effect out of scope variable - should not transform", () => {
|
|
22
|
+
const result = transform(`const someList = []
|
|
23
|
+
const first = [1, 2, 3].filter(x => {
|
|
24
|
+
someList.push(x);
|
|
25
|
+
})[0];`)
|
|
26
|
+
|
|
27
|
+
assert(!result.modified, "skip filter()[0] with named function predicate")
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test("predicate accessing outer scope - should not transform", () => {
|
|
31
|
+
const result = transform(`const first = [1, 2, 3].filter(x => x > threshold)[0];`)
|
|
32
|
+
|
|
33
|
+
assert(!result.modified, "skip filter()[0] when predicate accesses outer scope")
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test("predicate calling a function - should not transform", () => {
|
|
37
|
+
const result = transform(`const first = [1, 2, 3].filter(x => sideEffect(x))[0];`)
|
|
38
|
+
|
|
39
|
+
assert(!result.modified, "skip filter()[0] when predicate calls a function")
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test("predicate using computed member access - should not transform", () => {
|
|
43
|
+
const result = transform(`const first = [1, 2, 3].filter(x => x[0] > 0)[0];`)
|
|
44
|
+
|
|
45
|
+
assert(
|
|
46
|
+
!result.modified,
|
|
47
|
+
"skip filter()[0] when predicate uses computed member access",
|
|
48
|
+
)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test("predicate with multi-statement body - should not transform", () => {
|
|
52
|
+
const result = transform(
|
|
53
|
+
`const first = [1, 2, 3].filter(n => { const doubled = n * 2; return n > doubled; })[0];`,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
assert(
|
|
57
|
+
!result.modified,
|
|
58
|
+
"skip filter()[0] when predicate body has multiple statements",
|
|
59
|
+
)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test("predicate with member access on parameter", () => {
|
|
63
|
+
const result = transform(
|
|
64
|
+
`const first = ['ab', 'abc'].filter(x => x.length > 1)[0];`,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
assert(
|
|
68
|
+
result.modified,
|
|
69
|
+
"transform filter()[0] when predicate uses non-computed member access on parameter",
|
|
70
|
+
)
|
|
71
|
+
assert.match(result.code, /\.find\(x => x\.length > 1\)/)
|
|
72
|
+
assert.doesNotMatch(result.code, /filter/)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test("array literal with arrow function", () => {
|
|
76
|
+
const result = transform(`const first = [1, 2, 3].filter(x => x > 0)[0];`)
|
|
77
|
+
|
|
78
|
+
assert(result.modified, "transform filter()[0] with arrow function")
|
|
79
|
+
assert.match(result.code, /\[1, 2, 3\]\.find\(x => x > 0\)/)
|
|
80
|
+
assert.doesNotMatch(result.code, /filter/)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test("array literal with function expression", () => {
|
|
84
|
+
const result = transform(
|
|
85
|
+
`const first = [1, 2, 3].filter(function(n) { return n > 1; })[0];`,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
assert(result.modified, "transform filter()[0] with function expression")
|
|
89
|
+
assert.match(result.code, /\[1, 2, 3\]\.find\(/)
|
|
90
|
+
assert.doesNotMatch(result.code, /filter/)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test("new Array() with arrow function", () => {
|
|
94
|
+
const result = transform(
|
|
95
|
+
`const first = new Array(1, 2, 3).filter(n => n > 1)[0];`,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
assert(result.modified, "transform filter()[0] on new Array()")
|
|
99
|
+
assert.match(result.code, /new Array\(1, 2, 3\)\.find\(n => n > 1\)/)
|
|
100
|
+
assert.doesNotMatch(result.code, /filter/)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test("chained array method", () => {
|
|
104
|
+
const result = transform(
|
|
105
|
+
`const first = [1, 2, 3].map(n => n * 2).filter(n => n > 2)[0];`,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
assert(result.modified, "transform filter()[0] on chained array method")
|
|
109
|
+
assert.match(result.code, /\.find\(n => n > 2\)/)
|
|
110
|
+
assert.doesNotMatch(result.code, /filter/)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test("nested filter chain", () => {
|
|
114
|
+
const result = transform(
|
|
115
|
+
`const first = [1, 2, 3].filter(n => n > 0).filter(n => n < 3)[0];`,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
assert(result.modified, "transform filter()[0] on nested filter chain")
|
|
119
|
+
assert.match(result.code, /\.find\(n => n < 3\)/)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test("unknown receiver with arrow predicate - should not transform", () => {
|
|
123
|
+
const result = transform(`const first = arr.filter(n => n > 0)[0];`)
|
|
124
|
+
|
|
125
|
+
assert(
|
|
126
|
+
!result.modified,
|
|
127
|
+
"skip filter()[0] on unknown receiver even with inline predicate",
|
|
128
|
+
)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test("non-filter method with [0] - should not transform", () => {
|
|
132
|
+
const result = transform(`const first = [1, 2, 3].sort(fn)[0];`)
|
|
133
|
+
|
|
134
|
+
assert(!result.modified, "skip [0] access on non-filter method call")
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test("function call result with [0] - should not transform", () => {
|
|
138
|
+
const result = transform(`const first = getItems()[0];`)
|
|
139
|
+
|
|
140
|
+
assert(!result.modified, "skip [0] access on plain function call result")
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test("unknown identifier - should not transform", () => {
|
|
144
|
+
const result = transform(`const first = arr.filter(fn)[0];`)
|
|
145
|
+
|
|
146
|
+
assert(!result.modified, "skip filter()[0] on unknown identifier")
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
test("non-zero index - should not transform", () => {
|
|
150
|
+
const result = transform(`const second = [1, 2, 3].filter(fn)[1];`)
|
|
151
|
+
|
|
152
|
+
assert(!result.modified, "skip filter()[1] - not index 0")
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test("variable index - should not transform", () => {
|
|
156
|
+
const result = transform(`const item = [1, 2, 3].filter(fn)[n];`)
|
|
157
|
+
|
|
158
|
+
assert(!result.modified, "skip filter()[n] - computed variable index")
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
test("no arguments to filter - should not transform", () => {
|
|
162
|
+
const result = transform(`const first = [1, 2, 3].filter()[0];`)
|
|
163
|
+
|
|
164
|
+
assert(!result.modified, "skip filter()[0] with no filter arguments")
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test("two arguments to filter - should not transform", () => {
|
|
168
|
+
const result = transform(`const first = [1, 2, 3].filter(fn, thisArg)[0];`)
|
|
169
|
+
|
|
170
|
+
assert(!result.modified, "skip filter()[0] with two filter arguments")
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
})
|