miniread 1.117.0 → 1.118.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.
Files changed (43) hide show
  1. package/README.md +16 -0
  2. package/dist/transforms/preset-stats.json +2 -2
  3. package/dist/transforms-by-id/rename-add-event-listener-parameters/is-add-event-listener-key.d.ts +2 -2
  4. package/dist/transforms-by-id/rename-add-event-listener-parameters/manifest.json +3 -3
  5. package/dist/transforms-by-id/rename-add-event-listener-parameters/rename-add-event-listener-parameters-transform.js +18 -0
  6. package/dist/transforms-by-id/rename-catch-parameters/manifest.json +7 -7
  7. package/dist/transforms-by-id/rename-catch-parameters/rename-catch-parameters-transform.js +25 -16
  8. package/dist/transforms-by-id/rename-comparison-flags-v2/get-comparison-flag-base-name.d.ts +6 -0
  9. package/dist/transforms-by-id/rename-comparison-flags-v2/get-comparison-flag-base-name.js +93 -0
  10. package/dist/transforms-by-id/rename-comparison-flags-v2/get-string-comparison-details.d.ts +7 -0
  11. package/dist/transforms-by-id/rename-comparison-flags-v2/get-string-comparison-details.js +37 -0
  12. package/dist/transforms-by-id/rename-comparison-flags-v2/manifest.json +5 -5
  13. package/dist/transforms-by-id/rename-comparison-flags-v2/rename-comparison-flags-v2-transform.js +6 -73
  14. package/dist/transforms-by-id/rename-promise-catch-parameters/get-function-callback-from-identifier.d.ts +7 -0
  15. package/dist/transforms-by-id/rename-promise-catch-parameters/get-function-callback-from-identifier.js +78 -0
  16. package/dist/transforms-by-id/rename-promise-catch-parameters/get-promise-handler-callback-parameter-path.d.ts +6 -0
  17. package/dist/transforms-by-id/rename-promise-catch-parameters/get-promise-handler-callback-parameter-path.js +73 -0
  18. package/dist/transforms-by-id/rename-promise-catch-parameters/get-this-property-callback-path.d.ts +5 -0
  19. package/dist/transforms-by-id/rename-promise-catch-parameters/get-this-property-callback-path.js +75 -0
  20. package/dist/transforms-by-id/rename-promise-catch-parameters/manifest.json +6 -6
  21. package/dist/transforms-by-id/rename-promise-catch-parameters/rename-promise-catch-parameters-transform.js +1 -81
  22. package/dist/transforms-by-id/rename-promise-catch-parameters/unwrap-expression-path.d.ts +2 -0
  23. package/dist/transforms-by-id/rename-promise-catch-parameters/unwrap-expression-path.js +30 -0
  24. package/dist/transforms-by-id/rename-range-parameters/get-explicit-start-count-method-usage.js +16 -4
  25. package/dist/transforms-by-id/rename-range-parameters/get-range-parameter-names.js +1 -4
  26. package/dist/transforms-by-id/rename-range-parameters/get-range-parameters-from-pair.js +2 -4
  27. package/dist/transforms-by-id/rename-range-parameters/manifest.json +2 -2
  28. package/dist/transforms-by-id/rename-regex-builders/is-regex-builder-object.js +30 -13
  29. package/dist/transforms-by-id/rename-rest-pop-callbacks/manifest.json +6 -6
  30. package/dist/transforms-by-id/rename-rest-pop-callbacks/rename-rest-pop-callbacks-transform.js +26 -53
  31. package/dist/transforms-by-id/rename-worker-handles/manifest.json +6 -6
  32. package/dist/transforms-by-id/rename-worker-handles/rename-worker-handles-transform.js +28 -5
  33. package/dist/transforms-by-id/rename-zod-check-parameters/has-zod-check-context-signals.d.ts +2 -0
  34. package/dist/transforms-by-id/rename-zod-check-parameters/has-zod-check-context-signals.js +71 -0
  35. package/dist/transforms-by-id/rename-zod-check-parameters/is-zod-check-member.d.ts +3 -0
  36. package/dist/transforms-by-id/rename-zod-check-parameters/is-zod-check-member.js +16 -0
  37. package/dist/transforms-by-id/rename-zod-check-parameters/manifest.json +6 -6
  38. package/dist/transforms-by-id/rename-zod-check-parameters/rename-zod-check-parameters-transform.js +50 -15
  39. package/dist/transforms-by-id/simplify-string-trim/manifest.json +6 -6
  40. package/dist/transforms-by-id/simplify-string-trim/simplify-string-trim-transform.js +38 -6
  41. package/dist/transforms-by-id/split-variable-declarations/is-safe-to-split-variable-declaration.js +9 -2
  42. package/dist/transforms-by-id/split-variable-declarations/manifest.json +6 -6
  43. package/package.json +1 -1
package/README.md CHANGED
@@ -109,6 +109,22 @@ Development workflow/tooling documentation lives under `development-workflows/`:
109
109
  - `development-workflows/improve-transform-performance.md` — runtime performance optimization workflow
110
110
  - `development-workflows/improve-transform-coverage.md` — coverage improvement workflow
111
111
 
112
+ When you update transforms and want fresh recommended preset metrics, rerun the
113
+ recommended-preset evaluation:
114
+
115
+ ```bash
116
+ pnpm run miniread-evaluate -- --compare claude-code-2.1.10:claude-code-2.1.11 --baseline none --test recommended --update-manifest
117
+ ```
118
+
119
+ This updates `src/transforms/preset-stats.json` (look for
120
+ `Updated preset-stats.json` in CLI output).
121
+
122
+ Optional verification:
123
+
124
+ ```bash
125
+ git diff -- src/transforms/preset-stats.json
126
+ ```
127
+
112
128
  ## License
113
129
 
114
130
  MIT
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "recommended": {
3
- "diffSizePercent": 26.033752882035,
4
- "notes": "Measured with baseline none: 26.03% of original diff."
3
+ "diffSizePercent": 26.024303586952414,
4
+ "notes": "Measured with baseline none: 26.02% of original diff."
5
5
  }
6
6
  }
@@ -1,3 +1,3 @@
1
1
  import type { NodePath } from "@babel/traverse";
2
- import type { ObjectMethod, ObjectProperty } from "@babel/types";
3
- export declare const isAddEventListenerKey: (path: NodePath<ObjectMethod | ObjectProperty>) => boolean;
2
+ import type { ClassMethod, ClassProperty, ObjectMethod, ObjectProperty } from "@babel/types";
3
+ export declare const isAddEventListenerKey: (path: NodePath<ClassMethod | ClassProperty | ObjectMethod | ObjectProperty>) => boolean;
@@ -2,9 +2,9 @@
2
2
  "evaluations": {
3
3
  "claude-code-2.1.10:claude-code-2.1.11": {
4
4
  "diffSizePercent": 100,
5
- "evaluatedAt": "2026-02-10T06:04:51.282Z",
6
- "changedLines": 36,
7
- "durationSeconds": 29.137589833,
5
+ "evaluatedAt": "2026-02-21T17:39:30.353Z",
6
+ "changedLines": 44,
7
+ "durationSeconds": 145.734591643,
8
8
  "stableNames": 1359
9
9
  }
10
10
  },
@@ -67,6 +67,24 @@ export const renameAddEventListenerParametersTransform = {
67
67
  for (const fileInfo of getFilesToProcess(context)) {
68
68
  const group = new RenameGroup();
69
69
  traverse(fileInfo.ast, {
70
+ ClassMethod(path) {
71
+ nodesVisited++;
72
+ if (!isAddEventListenerKey(path))
73
+ return;
74
+ collectAddEventListenerRename(path, group);
75
+ },
76
+ ClassProperty(path) {
77
+ nodesVisited++;
78
+ if (!isAddEventListenerKey(path))
79
+ return;
80
+ const valuePath = path.get("value");
81
+ if (valuePath.isFunctionExpression()) {
82
+ collectAddEventListenerRename(valuePath, group);
83
+ }
84
+ else if (valuePath.isArrowFunctionExpression()) {
85
+ collectAddEventListenerRename(valuePath, group);
86
+ }
87
+ },
70
88
  ObjectMethod(path) {
71
89
  nodesVisited++;
72
90
  if (!isAddEventListenerKey(path))
@@ -1,14 +1,14 @@
1
1
  {
2
- "recommended": true,
3
- "recommendedOrder": 100,
4
2
  "notes": "Auto-added by evaluation script.",
5
3
  "evaluations": {
6
4
  "claude-code-2.1.10:claude-code-2.1.11": {
7
- "diffSizePercent": 99.92083686740175,
8
- "evaluatedAt": "2026-02-07T21:14:17.031Z",
9
- "changedLines": 7138,
10
- "durationSeconds": 150.31010159399997,
5
+ "diffSizePercent": 99.91495634425672,
6
+ "evaluatedAt": "2026-02-21T18:42:32.506Z",
7
+ "changedLines": 8006,
8
+ "durationSeconds": 57.912832917,
11
9
  "stableNames": 1359
12
10
  }
13
- }
11
+ },
12
+ "recommended": true,
13
+ "recommendedOrder": 100
14
14
  }
@@ -1,16 +1,23 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getBindingIdentifiers, isIdentifier } from "@babel/types";
2
3
  import { isStableRenamed, RenameGroup } from "../../core/stable-naming.js";
3
4
  import { getFilesToProcess, } from "../../core/types.js";
5
+ import { renamePromiseCatchParametersTransform } from "../rename-promise-catch-parameters/rename-promise-catch-parameters-transform.js";
4
6
  const require = createRequire(import.meta.url);
5
7
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
6
8
  const traverse = require("@babel/traverse").default;
7
9
  const BASE_NAME = "caughtError";
10
+ const getCatchParameterBindingIdentifiers = (parameter) => {
11
+ if (isIdentifier(parameter))
12
+ return [parameter];
13
+ return Object.values(getBindingIdentifiers(parameter));
14
+ };
8
15
  export const renameCatchParametersTransform = {
9
16
  id: "rename-catch-parameters",
10
17
  description: "Renames catch clause parameters to $caughtError/$caughtError2/...",
11
18
  scope: "file",
12
19
  parallelizable: true,
13
- transform(context) {
20
+ async transform(context) {
14
21
  let nodesVisited = 0;
15
22
  let transformationsApplied = 0;
16
23
  for (const fileInfo of getFilesToProcess(context)) {
@@ -21,24 +28,26 @@ export const renameCatchParametersTransform = {
21
28
  const parameter = path.node.param;
22
29
  if (!parameter)
23
30
  return;
24
- if (parameter.type !== "Identifier")
25
- return;
26
- const currentName = parameter.name;
27
- // Skip already-stable names
28
- if (isStableRenamed(currentName))
29
- return;
30
- group.add({
31
- scope: path.scope,
32
- currentName,
33
- baseName: BASE_NAME,
34
- });
31
+ const parameterIdentifiers = getCatchParameterBindingIdentifiers(parameter);
32
+ for (const parameterIdentifier of parameterIdentifiers) {
33
+ const currentName = parameterIdentifier.name;
34
+ // Skip already-stable names
35
+ if (isStableRenamed(currentName))
36
+ continue;
37
+ group.add({
38
+ scope: path.scope,
39
+ currentName,
40
+ baseName: BASE_NAME,
41
+ });
42
+ }
35
43
  },
36
44
  });
37
45
  transformationsApplied += group.apply();
38
46
  }
39
- return Promise.resolve({
40
- nodesVisited,
41
- transformationsApplied,
42
- });
47
+ const promiseCatchStats = await renamePromiseCatchParametersTransform.transform(context);
48
+ return {
49
+ nodesVisited: nodesVisited + promiseCatchStats.nodesVisited,
50
+ transformationsApplied: transformationsApplied + promiseCatchStats.transformationsApplied,
51
+ };
43
52
  },
44
53
  };
@@ -0,0 +1,6 @@
1
+ import type { Binding } from "@babel/traverse";
2
+ import type { Expression } from "@babel/types";
3
+ export declare const getComparisonFlagBaseName: (options: {
4
+ binding: Binding;
5
+ init: Expression;
6
+ }) => string | undefined;
@@ -0,0 +1,93 @@
1
+ import { getStringComparisonDetails, } from "./get-string-comparison-details.js";
2
+ const toCapitalizedComparisonValue = (stringValue) => {
3
+ const parts = stringValue.split(/[^\dA-Za-z]+/u).filter(Boolean);
4
+ if (parts.length === 0) {
5
+ return stringValue === "" ? "EmptyString" : "Flag";
6
+ }
7
+ return parts
8
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
9
+ .join("");
10
+ };
11
+ const getSpecificComparisonBaseName = (comparison) => {
12
+ const capitalizedValue = toCapitalizedComparisonValue(comparison.stringValue);
13
+ switch (comparison.operator) {
14
+ case "===": {
15
+ return `is${capitalizedValue}`;
16
+ }
17
+ case "!==": {
18
+ return `isNot${capitalizedValue}`;
19
+ }
20
+ case "==": {
21
+ return `equals${capitalizedValue}`;
22
+ }
23
+ case "!=": {
24
+ return `notEquals${capitalizedValue}`;
25
+ }
26
+ }
27
+ };
28
+ const collectComparisonWrites = (binding, initialComparison) => {
29
+ const comparisonWrites = [initialComparison];
30
+ if (binding.constant) {
31
+ return comparisonWrites;
32
+ }
33
+ for (const violationPath of binding.constantViolations) {
34
+ if (!violationPath.isAssignmentExpression()) {
35
+ return undefined;
36
+ }
37
+ const assignment = violationPath.node;
38
+ if (assignment.operator !== "=" ||
39
+ assignment.left.type !== "Identifier" ||
40
+ assignment.left.name !== binding.identifier.name) {
41
+ return undefined;
42
+ }
43
+ const reassignmentComparison = getStringComparisonDetails(assignment.right);
44
+ if (!reassignmentComparison) {
45
+ return undefined;
46
+ }
47
+ comparisonWrites.push(reassignmentComparison);
48
+ }
49
+ return comparisonWrites;
50
+ };
51
+ const getBaseNameForComparisonWrites = (comparisonWrites) => {
52
+ const firstWrite = comparisonWrites[0];
53
+ if (!firstWrite) {
54
+ return undefined;
55
+ }
56
+ if (comparisonWrites.every((comparison) => comparison.operator === firstWrite.operator &&
57
+ comparison.stringValue === firstWrite.stringValue)) {
58
+ // Keep name derivation aligned with rename-comparison-flags so v1/v2
59
+ // produce identical flag names for equivalent comparisons.
60
+ return getSpecificComparisonBaseName(firstWrite);
61
+ }
62
+ const allEqualityComparisons = comparisonWrites.every((comparison) => comparison.operator === "===" || comparison.operator === "==");
63
+ const allInequalityComparisons = comparisonWrites.every((comparison) => comparison.operator === "!==" || comparison.operator === "!=");
64
+ if (!allEqualityComparisons && !allInequalityComparisons) {
65
+ return undefined;
66
+ }
67
+ const singleStringValue = comparisonWrites.every((comparison) => comparison.stringValue === firstWrite.stringValue);
68
+ if (singleStringValue) {
69
+ const capitalizedValue = toCapitalizedComparisonValue(firstWrite.stringValue);
70
+ if (allEqualityComparisons) {
71
+ return `matches${capitalizedValue}`;
72
+ }
73
+ return `doesNotMatch${capitalizedValue}`;
74
+ }
75
+ if (allEqualityComparisons) {
76
+ return "matchesString";
77
+ }
78
+ return "doesNotMatchString";
79
+ };
80
+ export const getComparisonFlagBaseName = (options) => {
81
+ const { binding, init } = options;
82
+ const comparisonDetails = getStringComparisonDetails(init);
83
+ if (!comparisonDetails) {
84
+ return undefined;
85
+ }
86
+ if (binding.constant) {
87
+ return getSpecificComparisonBaseName(comparisonDetails);
88
+ }
89
+ const comparisonWrites = collectComparisonWrites(binding, comparisonDetails);
90
+ return comparisonWrites
91
+ ? (getBaseNameForComparisonWrites(comparisonWrites) ?? "comparisonResult")
92
+ : "comparisonResult";
93
+ };
@@ -0,0 +1,7 @@
1
+ import type { Expression } from "@babel/types";
2
+ export type ComparisonOperator = "===" | "!==" | "==" | "!=";
3
+ export type StringComparisonDetails = {
4
+ operator: ComparisonOperator;
5
+ stringValue: string;
6
+ };
7
+ export declare const getStringComparisonDetails: (expression: Expression) => StringComparisonDetails | undefined;
@@ -0,0 +1,37 @@
1
+ const isSupportedComparisonSide = (expression) => {
2
+ if (expression.type === "PrivateName") {
3
+ return false;
4
+ }
5
+ if (expression.type === "TemplateLiteral") {
6
+ return expression.expressions.length > 0;
7
+ }
8
+ if (expression.type.endsWith("Literal")) {
9
+ return false;
10
+ }
11
+ return true;
12
+ };
13
+ export const getStringComparisonDetails = (expression) => {
14
+ if (expression.type !== "BinaryExpression") {
15
+ return undefined;
16
+ }
17
+ const operator = expression.operator;
18
+ if (operator !== "===" &&
19
+ operator !== "!==" &&
20
+ operator !== "==" &&
21
+ operator !== "!=") {
22
+ return undefined;
23
+ }
24
+ const leftValue = expression.left.type === "StringLiteral"
25
+ ? expression.left.value
26
+ : undefined;
27
+ const rightValue = expression.right.type === "StringLiteral"
28
+ ? expression.right.value
29
+ : undefined;
30
+ if (leftValue !== undefined && isSupportedComparisonSide(expression.right)) {
31
+ return { operator, stringValue: leftValue };
32
+ }
33
+ if (rightValue !== undefined && isSupportedComparisonSide(expression.left)) {
34
+ return { operator, stringValue: rightValue };
35
+ }
36
+ return undefined;
37
+ };
@@ -2,11 +2,11 @@
2
2
  "notes": "Supersedes rename-comparison-flags in the recommended preset and extends comparison flag renaming to member expressions like 'client.config.type === \"sse\"'.",
3
3
  "evaluations": {
4
4
  "claude-code-2.1.10:claude-code-2.1.11": {
5
- "diffSizePercent": 99.98869098105739,
6
- "evaluatedAt": "2026-02-09T14:01:54.339Z",
7
- "changedLines": 550,
8
- "durationSeconds": 240.26302797699998,
9
- "stableNames": 1416
5
+ "diffSizePercent": 99.98299126885134,
6
+ "evaluatedAt": "2026-02-21T18:28:53.452Z",
7
+ "changedLines": 810,
8
+ "durationSeconds": 41.915363875,
9
+ "stableNames": 1429
10
10
  }
11
11
  },
12
12
  "recommended": true,
@@ -1,6 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { RenameGroup, isStableRenamed } from "../../core/stable-naming.js";
3
3
  import { getFilesToProcess } from "../../core/types.js";
4
+ import { getComparisonFlagBaseName } from "./get-comparison-flag-base-name.js";
4
5
  const require = createRequire(import.meta.url);
5
6
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
6
7
  const traverse = require("@babel/traverse").default;
@@ -21,86 +22,18 @@ export const renameComparisonFlagsV2Transform = {
21
22
  if (!init || id.type !== "Identifier") {
22
23
  return;
23
24
  }
24
- if (init.type !== "BinaryExpression" ||
25
- (init.operator !== "===" &&
26
- init.operator !== "!==" &&
27
- init.operator !== "==" &&
28
- init.operator !== "!=")) {
29
- return;
30
- }
31
- let stringValue;
32
- let otherSide;
33
- if (init.left.type === "StringLiteral") {
34
- stringValue = init.left.value;
35
- otherSide = init.right;
36
- }
37
- else if (init.right.type === "StringLiteral") {
38
- stringValue = init.right.value;
39
- otherSide = init.left;
40
- }
41
- if (!otherSide || stringValue === undefined) {
42
- return;
43
- }
44
- let isSupportedComparisonSide = false;
45
- if (otherSide.type === "Identifier") {
46
- isSupportedComparisonSide = true;
47
- }
48
- else if (otherSide.type === "MemberExpression" ||
49
- otherSide.type === "OptionalMemberExpression") {
50
- const hasLiteralComputedProperty = otherSide.computed &&
51
- (otherSide.property.type === "StringLiteral" ||
52
- otherSide.property.type === "NumericLiteral");
53
- isSupportedComparisonSide =
54
- // Allow dot access and literal bracket access (e.g. obj["type"]).
55
- !otherSide.computed || hasLiteralComputedProperty;
56
- }
57
- // Also excludes constant string-vs-string comparisons (e.g. "a" === "b"),
58
- // which are not variable flag checks.
59
- if (!isSupportedComparisonSide) {
25
+ const currentName = id.name;
26
+ if (isStableRenamed(currentName)) {
60
27
  return;
61
28
  }
62
- const currentName = id.name;
63
29
  const binding = path.scope.getBinding(currentName);
64
- // Prefer high-certainty names over wider coverage: non-constant flags
65
- // can later hold unrelated values, making semantic renames misleading.
66
- if (!binding?.constant) {
30
+ if (!binding) {
67
31
  return;
68
32
  }
69
- if (isStableRenamed(currentName)) {
33
+ const baseName = getComparisonFlagBaseName({ binding, init });
34
+ if (!baseName) {
70
35
  return;
71
36
  }
72
- const operator = init.operator;
73
- // Keep name derivation aligned with rename-comparison-flags so v1/v2
74
- // produce identical flag names for equivalent comparisons.
75
- const parts = stringValue
76
- .split(/[^\dA-Za-z]+/u)
77
- .filter(Boolean);
78
- const capitalizedValue = parts.length === 0
79
- ? stringValue === ""
80
- ? "EmptyString"
81
- : "Flag"
82
- : parts
83
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
84
- .join("");
85
- let baseName;
86
- switch (operator) {
87
- case "===": {
88
- baseName = `is${capitalizedValue}`;
89
- break;
90
- }
91
- case "!==": {
92
- baseName = `isNot${capitalizedValue}`;
93
- break;
94
- }
95
- case "==": {
96
- baseName = `equals${capitalizedValue}`;
97
- break;
98
- }
99
- case "!=": {
100
- baseName = `notEquals${capitalizedValue}`;
101
- break;
102
- }
103
- }
104
37
  renameGroup.add({
105
38
  scope: binding.scope,
106
39
  currentName,
@@ -0,0 +1,7 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { ArrowFunctionExpression, FunctionDeclaration, FunctionExpression, Identifier } from "@babel/types";
3
+ type FunctionCallbackPath = NodePath<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression>;
4
+ type IdentifierPath = NodePath<Identifier>;
5
+ type VisitedNames = Set<string>;
6
+ export declare const getFunctionCallbackFromIdentifier: (argumentPath: IdentifierPath, callSiteStart?: number, visitedNames?: VisitedNames) => FunctionCallbackPath | undefined;
7
+ export {};
@@ -0,0 +1,78 @@
1
+ import { unwrapExpressionPath } from "./unwrap-expression-path.js";
2
+ const getFunctionCallbackFromAssignment = (assignmentPath, identifierName, callSiteStart, visitedNames) => {
3
+ if (assignmentPath.node.operator !== "=")
4
+ return;
5
+ const leftPath = assignmentPath.get("left");
6
+ if (Array.isArray(leftPath))
7
+ return;
8
+ if (!leftPath.isIdentifier({ name: identifierName }))
9
+ return;
10
+ const rightPath = assignmentPath.get("right");
11
+ if (Array.isArray(rightPath))
12
+ return;
13
+ const callbackPath = unwrapExpressionPath(rightPath);
14
+ if (!callbackPath)
15
+ return;
16
+ if (!callbackPath.isFunctionExpression() &&
17
+ !callbackPath.isArrowFunctionExpression()) {
18
+ if (!callbackPath.isIdentifier())
19
+ return;
20
+ return getFunctionCallbackFromIdentifier(callbackPath, callSiteStart, visitedNames);
21
+ }
22
+ return callbackPath;
23
+ };
24
+ const isIdentifierReassignment = (assignmentPath, identifierName) => {
25
+ if (assignmentPath.node.operator !== "=")
26
+ return false;
27
+ const leftPath = assignmentPath.get("left");
28
+ if (Array.isArray(leftPath))
29
+ return false;
30
+ return leftPath.isIdentifier({ name: identifierName });
31
+ };
32
+ export const getFunctionCallbackFromIdentifier = (argumentPath, callSiteStart, visitedNames = new Set()) => {
33
+ const identifierName = argumentPath.node.name;
34
+ if (visitedNames.has(identifierName))
35
+ return;
36
+ const nextVisitedNames = new Set(visitedNames);
37
+ nextVisitedNames.add(identifierName);
38
+ const binding = argumentPath.scope.getBinding(argumentPath.node.name);
39
+ if (!binding)
40
+ return;
41
+ const bindingPath = binding.path;
42
+ if (bindingPath.isFunctionDeclaration()) {
43
+ return bindingPath;
44
+ }
45
+ if (!bindingPath.isVariableDeclarator())
46
+ return;
47
+ const initPath = bindingPath.get("init");
48
+ if (Array.isArray(initPath))
49
+ return;
50
+ let candidateCallbackPath;
51
+ const initCallbackPath = unwrapExpressionPath(initPath);
52
+ if (initCallbackPath &&
53
+ (initCallbackPath.isFunctionExpression() ||
54
+ initCallbackPath.isArrowFunctionExpression())) {
55
+ const initStart = initCallbackPath.node.start;
56
+ if (typeof callSiteStart !== "number" ||
57
+ typeof initStart !== "number" ||
58
+ initStart < callSiteStart) {
59
+ candidateCallbackPath = initCallbackPath;
60
+ }
61
+ }
62
+ for (const violationPath of binding.constantViolations) {
63
+ if (!violationPath.isAssignmentExpression())
64
+ continue;
65
+ const violationStart = violationPath.node.start;
66
+ if (typeof callSiteStart === "number" &&
67
+ typeof violationStart === "number" &&
68
+ violationStart >= callSiteStart) {
69
+ continue;
70
+ }
71
+ if (!isIdentifierReassignment(violationPath, argumentPath.node.name)) {
72
+ continue;
73
+ }
74
+ const assignedCallbackPath = getFunctionCallbackFromAssignment(violationPath, argumentPath.node.name, violationStart ?? callSiteStart, nextVisitedNames);
75
+ candidateCallbackPath = assignedCallbackPath;
76
+ }
77
+ return candidateCallbackPath;
78
+ };
@@ -0,0 +1,6 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { CallExpression, Identifier, OptionalCallExpression } from "@babel/types";
3
+ type IdentifierPath = NodePath<Identifier>;
4
+ type CatchCallPath = NodePath<CallExpression> | NodePath<OptionalCallExpression>;
5
+ export declare const getPromiseHandlerCallbackParameterPath: (path: CatchCallPath) => IdentifierPath | undefined;
6
+ export {};
@@ -0,0 +1,73 @@
1
+ import { getFunctionCallbackFromIdentifier } from "./get-function-callback-from-identifier.js";
2
+ import { getThisPropertyCallbackPath } from "./get-this-property-callback-path.js";
3
+ import { unwrapExpressionPath } from "./unwrap-expression-path.js";
4
+ const getPromiseHandlerName = (callee) => {
5
+ if (callee.type !== "MemberExpression" &&
6
+ callee.type !== "OptionalMemberExpression") {
7
+ return;
8
+ }
9
+ const property = callee.property;
10
+ if (callee.computed) {
11
+ if (property.type !== "StringLiteral")
12
+ return;
13
+ if (property.value === "catch" || property.value === "then") {
14
+ return property.value;
15
+ }
16
+ return;
17
+ }
18
+ if (property.type !== "Identifier")
19
+ return;
20
+ if (property.name === "catch" || property.name === "then") {
21
+ return property.name;
22
+ }
23
+ };
24
+ const getFirstCallbackParameterPath = (callbackPath) => {
25
+ if (callbackPath.node.params.length === 0)
26
+ return;
27
+ const parameterPath = callbackPath.get("params.0");
28
+ if (Array.isArray(parameterPath))
29
+ return;
30
+ if (parameterPath.isIdentifier()) {
31
+ return parameterPath;
32
+ }
33
+ if (!parameterPath.isAssignmentPattern())
34
+ return;
35
+ const leftPath = parameterPath.get("left");
36
+ if (Array.isArray(leftPath))
37
+ return;
38
+ if (!leftPath.isIdentifier())
39
+ return;
40
+ return leftPath;
41
+ };
42
+ export const getPromiseHandlerCallbackParameterPath = (path) => {
43
+ const handlerName = getPromiseHandlerName(path.node.callee);
44
+ if (!handlerName)
45
+ return;
46
+ const argumentIndex = handlerName === "catch" ? 0 : 1;
47
+ const argument = path.node.arguments[argumentIndex];
48
+ if (!argument)
49
+ return;
50
+ const argumentPath = path.get(`arguments.${argumentIndex}`);
51
+ if (Array.isArray(argumentPath))
52
+ return;
53
+ const callbackPath = unwrapExpressionPath(argumentPath);
54
+ if (!callbackPath)
55
+ return;
56
+ if (callbackPath.isFunctionExpression() ||
57
+ callbackPath.isArrowFunctionExpression()) {
58
+ return getFirstCallbackParameterPath(callbackPath);
59
+ }
60
+ const callSiteStart = path.node.start ?? undefined;
61
+ if (callbackPath.isIdentifier()) {
62
+ const referencedCallbackPath = getFunctionCallbackFromIdentifier(callbackPath, callSiteStart);
63
+ if (!referencedCallbackPath)
64
+ return;
65
+ return getFirstCallbackParameterPath(referencedCallbackPath);
66
+ }
67
+ if (!callbackPath.isMemberExpression())
68
+ return;
69
+ const thisPropertyCallbackPath = getThisPropertyCallbackPath(callbackPath, callSiteStart);
70
+ if (!thisPropertyCallbackPath)
71
+ return;
72
+ return getFirstCallbackParameterPath(thisPropertyCallbackPath);
73
+ };
@@ -0,0 +1,5 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { ArrowFunctionExpression, FunctionDeclaration, FunctionExpression, MemberExpression } from "@babel/types";
3
+ type FunctionCallbackPath = NodePath<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression>;
4
+ export declare const getThisPropertyCallbackPath: (argumentPath: NodePath<MemberExpression>, callSiteStart?: number) => FunctionCallbackPath | undefined;
5
+ export {};