miniread 1.0.0 → 1.1.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 (91) hide show
  1. package/README.md +2 -2
  2. package/dist/scripts/evaluate/check-expected-evaluations.d.ts +17 -0
  3. package/dist/scripts/evaluate/check-expected-evaluations.js +35 -0
  4. package/dist/scripts/evaluate/check-snapshots.d.ts +14 -0
  5. package/dist/scripts/evaluate/check-snapshots.js +111 -0
  6. package/dist/scripts/evaluate/clone-metrics-report.d.ts +2 -0
  7. package/dist/scripts/evaluate/clone-metrics-report.js +6 -0
  8. package/dist/scripts/evaluate/create-evaluate-command.js +5 -20
  9. package/dist/scripts/evaluate/evaluate-help-text.d.ts +1 -0
  10. package/dist/scripts/evaluate/evaluate-help-text.js +21 -0
  11. package/dist/scripts/evaluate/pair-evaluator.d.ts +1 -0
  12. package/dist/scripts/evaluate/pair-evaluator.js +14 -3
  13. package/dist/scripts/evaluate/parse-evaluate-cli-options.d.ts +2 -0
  14. package/dist/scripts/evaluate/parse-evaluate-cli-options.js +3 -1
  15. package/dist/scripts/evaluate/parse-transform-manifest.d.ts +1 -1
  16. package/dist/scripts/evaluate/parse-transform-manifest.js +1 -1
  17. package/dist/scripts/evaluate/relative-patch-path.d.ts +7 -0
  18. package/dist/scripts/evaluate/relative-patch-path.js +22 -0
  19. package/dist/scripts/evaluate/run-check-mode.d.ts +8 -0
  20. package/dist/scripts/evaluate/run-check-mode.js +116 -0
  21. package/dist/scripts/evaluate/run-evaluate-cli.js +11 -3
  22. package/dist/scripts/evaluate/run-evaluations.js +1 -0
  23. package/dist/scripts/evaluate/transform-content.d.ts +14 -0
  24. package/dist/scripts/evaluate/transform-content.js +31 -0
  25. package/dist/scripts/evaluate/transform-manifest.d.ts +0 -2
  26. package/dist/scripts/evaluate/transform-manifest.js +15 -34
  27. package/dist/transforms/expand-return-sequence/expand-return-sequence-transform.d.ts +2 -0
  28. package/dist/transforms/expand-return-sequence/expand-return-sequence-transform.js +91 -0
  29. package/dist/transforms/expand-sequence-expressions/expand-expression-statement-sequence.d.ts +3 -0
  30. package/dist/transforms/expand-sequence-expressions/expand-expression-statement-sequence.js +57 -0
  31. package/dist/transforms/expand-sequence-expressions/expand-sequence-expressions-transform.d.ts +2 -0
  32. package/dist/transforms/expand-sequence-expressions/expand-sequence-expressions-transform.js +34 -0
  33. package/dist/transforms/expand-sequence-expressions/expand-variable-declaration-sequence.d.ts +3 -0
  34. package/dist/transforms/expand-sequence-expressions/expand-variable-declaration-sequence.js +93 -0
  35. package/dist/transforms/expand-sequence-expressions-v2/expand-expression-statement-sequence.d.ts +3 -0
  36. package/dist/transforms/expand-sequence-expressions-v2/expand-expression-statement-sequence.js +55 -0
  37. package/dist/transforms/expand-sequence-expressions-v2/expand-return-sequence.d.ts +3 -0
  38. package/dist/transforms/expand-sequence-expressions-v2/expand-return-sequence.js +86 -0
  39. package/dist/transforms/expand-sequence-expressions-v2/expand-sequence-expressions-v2-transform.d.ts +2 -0
  40. package/dist/transforms/expand-sequence-expressions-v2/expand-sequence-expressions-v2-transform.js +41 -0
  41. package/dist/transforms/expand-sequence-expressions-v2/expand-variable-declaration-sequence.d.ts +3 -0
  42. package/dist/transforms/expand-sequence-expressions-v2/expand-variable-declaration-sequence.js +93 -0
  43. package/dist/transforms/expand-sequence-expressions-v3/expand-expression-statement-sequence.d.ts +3 -0
  44. package/dist/transforms/expand-sequence-expressions-v3/expand-expression-statement-sequence.js +64 -0
  45. package/dist/transforms/expand-sequence-expressions-v3/expand-return-sequence.d.ts +3 -0
  46. package/dist/transforms/expand-sequence-expressions-v3/expand-return-sequence.js +91 -0
  47. package/dist/transforms/expand-sequence-expressions-v3/expand-sequence-expressions-v3-transform.d.ts +2 -0
  48. package/dist/transforms/expand-sequence-expressions-v3/expand-sequence-expressions-v3-transform.js +48 -0
  49. package/dist/transforms/expand-sequence-expressions-v3/expand-throw-sequence.d.ts +3 -0
  50. package/dist/transforms/expand-sequence-expressions-v3/expand-throw-sequence.js +101 -0
  51. package/dist/transforms/expand-sequence-expressions-v3/expand-variable-declaration-sequence.d.ts +3 -0
  52. package/dist/transforms/expand-sequence-expressions-v3/expand-variable-declaration-sequence.js +99 -0
  53. package/dist/transforms/expand-throw-sequence/expand-throw-sequence-transform.d.ts +2 -0
  54. package/dist/transforms/expand-throw-sequence/expand-throw-sequence-transform.js +117 -0
  55. package/dist/transforms/rename-destructured-aliases/binding-context.d.ts +7 -0
  56. package/dist/transforms/rename-destructured-aliases/binding-context.js +83 -0
  57. package/dist/transforms/rename-destructured-aliases/rename-destructured-aliases-transform.d.ts +2 -0
  58. package/dist/transforms/rename-destructured-aliases/rename-destructured-aliases-transform.js +118 -0
  59. package/dist/transforms/rename-promise-executor-parameters/is-valid-binding-identifier.d.ts +1 -0
  60. package/dist/transforms/rename-promise-executor-parameters/is-valid-binding-identifier.js +10 -0
  61. package/dist/transforms/rename-promise-executor-parameters/promise-executor-heuristics.d.ts +6 -0
  62. package/dist/transforms/rename-promise-executor-parameters/promise-executor-heuristics.js +88 -0
  63. package/dist/transforms/rename-promise-executor-parameters/rename-binding-if-safe.d.ts +3 -0
  64. package/dist/transforms/rename-promise-executor-parameters/rename-binding-if-safe.js +40 -0
  65. package/dist/transforms/rename-promise-executor-parameters/rename-promise-executor-parameters-transform.d.ts +2 -0
  66. package/dist/transforms/rename-promise-executor-parameters/rename-promise-executor-parameters-transform.js +76 -0
  67. package/dist/transforms/rename-use-reference-guards/get-member-expression-for-reference.d.ts +3 -0
  68. package/dist/transforms/rename-use-reference-guards/get-member-expression-for-reference.js +10 -0
  69. package/dist/transforms/rename-use-reference-guards/get-reference-usage.d.ts +9 -0
  70. package/dist/transforms/rename-use-reference-guards/get-reference-usage.js +47 -0
  71. package/dist/transforms/rename-use-reference-guards/get-target-name.d.ts +2 -0
  72. package/dist/transforms/rename-use-reference-guards/get-target-name.js +23 -0
  73. package/dist/transforms/rename-use-reference-guards/is-use-reference-false-initializer.d.ts +2 -0
  74. package/dist/transforms/rename-use-reference-guards/is-use-reference-false-initializer.js +25 -0
  75. package/dist/transforms/rename-use-reference-guards/is-valid-binding-identifier.d.ts +1 -0
  76. package/dist/transforms/rename-use-reference-guards/is-valid-binding-identifier.js +10 -0
  77. package/dist/transforms/rename-use-reference-guards/rename-use-reference-guards-transform.d.ts +3 -0
  78. package/dist/transforms/rename-use-reference-guards/rename-use-reference-guards-transform.js +55 -0
  79. package/dist/transforms/split-variable-declarations/create-variable-declaration.d.ts +2 -0
  80. package/dist/transforms/split-variable-declarations/create-variable-declaration.js +28 -0
  81. package/dist/transforms/split-variable-declarations/is-safe-to-split-variable-declaration.d.ts +3 -0
  82. package/dist/transforms/split-variable-declarations/is-safe-to-split-variable-declaration.js +52 -0
  83. package/dist/transforms/split-variable-declarations/split-variable-declarations-transform.d.ts +2 -0
  84. package/dist/transforms/split-variable-declarations/split-variable-declarations-transform.js +81 -0
  85. package/dist/transforms/transform-registry.js +18 -4
  86. package/package.json +29 -23
  87. package/transform-manifest.json +96 -26
  88. package/dist/transforms/add-prefix/add-prefix-transform.d.ts +0 -2
  89. package/dist/transforms/add-prefix/add-prefix-transform.js +0 -40
  90. package/dist/transforms/add-suffix/add-suffix-transform.d.ts +0 -2
  91. package/dist/transforms/add-suffix/add-suffix-transform.js +0 -40
@@ -12,7 +12,7 @@ const average = (values) => {
12
12
  return total / values.length;
13
13
  };
14
14
  export const updateTransformManifestFromEvaluation = async (options) => {
15
- const { manifestPath, baselineTransforms, testTransforms, reductionRatios, transformRegistry, } = options;
15
+ const { manifestPath, baselineTransforms, testTransforms, reductionRatios } = options;
16
16
  try {
17
17
  await fs.access(manifestPath);
18
18
  }
@@ -26,34 +26,10 @@ export const updateTransformManifestFromEvaluation = async (options) => {
26
26
  const existingIds = new Set(manifest.transforms.map((t) => t.id));
27
27
  const baselineIds = normalizeTransformIds(baselineTransforms);
28
28
  const testIds = normalizeTransformIds(testTransforms);
29
- const idsToEnsure = new Set([...baselineIds, ...testIds]);
30
- idsToEnsure.delete("none");
31
- let maxIteration = 0;
32
- for (const entry of manifest.transforms) {
33
- maxIteration = Math.max(maxIteration, entry.iteration);
34
- }
35
- let nextIteration = maxIteration + 1;
29
+ const idsToUpdate = new Set([...baselineIds, ...testIds]);
30
+ idsToUpdate.delete("none");
31
+ const missingIds = [...idsToUpdate].filter((id) => !existingIds.has(id));
36
32
  let updated = false;
37
- for (const transformId of idsToEnsure) {
38
- if (existingIds.has(transformId))
39
- continue;
40
- const transform = transformRegistry[transformId];
41
- const description = transform?.description ?? `Auto-added by evaluation: ${transformId}`;
42
- const scope = transform?.scope ?? "file";
43
- const parallelizable = transform?.parallelizable ?? true;
44
- manifest.transforms.push({
45
- id: transformId,
46
- description,
47
- iteration: nextIteration++,
48
- scope,
49
- parallelizable,
50
- diffReductionImpact: 0,
51
- enabledByDefault: false,
52
- notes: "Auto-added by evaluation script.",
53
- });
54
- existingIds.add(transformId);
55
- updated = true;
56
- }
57
33
  const isBaselineNone = baselineIds.length === 0 ||
58
34
  (baselineTransforms.length === 1 && baselineTransforms[0] === "none");
59
35
  if (isBaselineNone && testIds.length === 1) {
@@ -63,10 +39,6 @@ export const updateTransformManifestFromEvaluation = async (options) => {
63
39
  const entry = manifest.transforms.find((t) => t.id === transformId);
64
40
  if (entry && entry.diffReductionImpact !== measuredImpact) {
65
41
  entry.diffReductionImpact = measuredImpact;
66
- const measurementNote = `Measured with baseline none: ${(measuredImpact * 100).toFixed(2)}%.`;
67
- entry.notes = entry.notes
68
- ? `${entry.notes} ${measurementNote}`
69
- : measurementNote;
70
42
  updated = true;
71
43
  }
72
44
  }
@@ -92,11 +64,20 @@ export const updateTransformManifestFromEvaluation = async (options) => {
92
64
  }
93
65
  }
94
66
  if (!updated) {
67
+ const missingMessage = missingIds.length > 0
68
+ ? ` Missing transform entries: ${missingIds.join(", ")}. Add them to transform-manifest.json manually.`
69
+ : "";
95
70
  return {
96
71
  updated: false,
97
- message: "transform-manifest.json already up to date",
72
+ message: `transform-manifest.json already up to date.${missingMessage}`,
98
73
  };
99
74
  }
100
75
  await writeJsonFileAtomic(manifestPath, manifest, { overwrite: true });
101
- return { updated: true, message: "Updated transform-manifest.json" };
76
+ const missingMessage = missingIds.length > 0
77
+ ? ` Missing transform entries: ${missingIds.join(", ")}. Add them to transform-manifest.json manually.`
78
+ : "";
79
+ return {
80
+ updated: true,
81
+ message: `Updated transform-manifest.json.${missingMessage}`,
82
+ };
102
83
  };
@@ -0,0 +1,2 @@
1
+ import type { Transform } from "../../core/types.js";
2
+ export declare const expandReturnSequenceTransform: Transform;
@@ -0,0 +1,91 @@
1
+ import { createRequire } from "node:module";
2
+ const require = createRequire(import.meta.url);
3
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
4
+ const traverse = require("@babel/traverse").default;
5
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
6
+ const t = require("@babel/types");
7
+ const isSupportedParent = (path) => {
8
+ return path.parentPath.isBlockStatement() || path.parentPath.isSwitchCase();
9
+ };
10
+ const getSequenceExpressions = (node) => {
11
+ const argument = node.argument;
12
+ if (!argument)
13
+ return;
14
+ if (argument.type !== "SequenceExpression")
15
+ return;
16
+ if (argument.expressions.length < 2)
17
+ return;
18
+ return argument.expressions;
19
+ };
20
+ const createExpressionStatement = (expression) => {
21
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
22
+ return t.expressionStatement(expression);
23
+ };
24
+ const createReturnStatement = (expression) => {
25
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
26
+ return t.returnStatement(expression);
27
+ };
28
+ const isDirectiveProloguePosition = (path) => {
29
+ if (!path.parentPath.isBlockStatement())
30
+ return false;
31
+ const siblings = path.parentPath.get("body");
32
+ if (!Array.isArray(siblings))
33
+ return false;
34
+ const index = siblings.findIndex((sibling) => sibling.node === path.node);
35
+ if (index === -1)
36
+ return false;
37
+ for (let bodyIndex = 0; bodyIndex < index; bodyIndex++) {
38
+ const sibling = siblings[bodyIndex];
39
+ if (!sibling)
40
+ return false;
41
+ if (!sibling.isExpressionStatement())
42
+ return false;
43
+ if (sibling.node.expression.type !== "StringLiteral")
44
+ return false;
45
+ }
46
+ return true;
47
+ };
48
+ export const expandReturnSequenceTransform = {
49
+ id: "expand-return-sequence",
50
+ description: "Expands return sequences like `return a(), b();`",
51
+ scope: "file",
52
+ parallelizable: true,
53
+ transform(context) {
54
+ const { projectGraph } = context;
55
+ let nodesVisited = 0;
56
+ let transformationsApplied = 0;
57
+ for (const [, fileInfo] of projectGraph.files) {
58
+ traverse(fileInfo.ast, {
59
+ ReturnStatement(path) {
60
+ nodesVisited++;
61
+ if (!isSupportedParent(path))
62
+ return;
63
+ const expressions = getSequenceExpressions(path.node);
64
+ if (!expressions)
65
+ return;
66
+ const leadingExpressions = expressions.slice(0, -1);
67
+ const lastExpression = expressions.at(-1);
68
+ if (!lastExpression)
69
+ return;
70
+ const firstExpression = leadingExpressions[0];
71
+ if (firstExpression?.type === "StringLiteral" &&
72
+ isDirectiveProloguePosition(path)) {
73
+ return;
74
+ }
75
+ const statements = leadingExpressions.map((expression) => createExpressionStatement(
76
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
77
+ t.cloneNode(expression, true)));
78
+ statements.push(createReturnStatement(
79
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
80
+ t.cloneNode(lastExpression, true)));
81
+ path.replaceWithMultiple(statements);
82
+ transformationsApplied++;
83
+ },
84
+ });
85
+ }
86
+ return Promise.resolve({
87
+ nodesVisited,
88
+ transformationsApplied,
89
+ });
90
+ },
91
+ };
@@ -0,0 +1,3 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { ExpressionStatement } from "@babel/types";
3
+ export declare const tryExpandExpressionStatementSequence: (path: NodePath<ExpressionStatement>) => boolean;
@@ -0,0 +1,57 @@
1
+ import { createRequire } from "node:module";
2
+ const require = createRequire(import.meta.url);
3
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
4
+ const t = require("@babel/types");
5
+ const isSupportedStatementContainer = (path) => {
6
+ const parentPath = path.parentPath;
7
+ if (!parentPath)
8
+ return false;
9
+ return (parentPath.isProgram() ||
10
+ parentPath.isBlockStatement() ||
11
+ parentPath.isSwitchCase());
12
+ };
13
+ const isDirectiveProloguePosition = (path) => {
14
+ const parentPath = path.parentPath;
15
+ if (!parentPath)
16
+ return false;
17
+ if (!parentPath.isProgram() && !parentPath.isBlockStatement())
18
+ return false;
19
+ const siblings = parentPath.get("body");
20
+ if (!Array.isArray(siblings))
21
+ return false;
22
+ const index = siblings.findIndex((sibling) => sibling.node === path.node);
23
+ if (index === -1)
24
+ return false;
25
+ for (let bodyIndex = 0; bodyIndex < index; bodyIndex++) {
26
+ const sibling = siblings[bodyIndex];
27
+ if (!sibling)
28
+ return false;
29
+ if (!sibling.isExpressionStatement())
30
+ return false;
31
+ if (sibling.node.expression.type !== "StringLiteral")
32
+ return false;
33
+ }
34
+ return true;
35
+ };
36
+ export const tryExpandExpressionStatementSequence = (path) => {
37
+ if (!isSupportedStatementContainer(path))
38
+ return false;
39
+ const expression = path.node.expression;
40
+ if (expression.type !== "SequenceExpression")
41
+ return false;
42
+ if (expression.expressions.length < 2)
43
+ return false;
44
+ const firstExpression = expression.expressions[0];
45
+ if (firstExpression?.type === "StringLiteral" &&
46
+ isDirectiveProloguePosition(path)) {
47
+ return false;
48
+ }
49
+ const statements = expression.expressions.map((expr) => {
50
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
51
+ return t.expressionStatement(
52
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
53
+ t.cloneNode(expr, true));
54
+ });
55
+ path.replaceWithMultiple(statements);
56
+ return true;
57
+ };
@@ -0,0 +1,2 @@
1
+ import type { Transform } from "../../core/types.js";
2
+ export declare const expandSequenceExpressionsTransform: Transform;
@@ -0,0 +1,34 @@
1
+ import { createRequire } from "node:module";
2
+ import { tryExpandExpressionStatementSequence } from "./expand-expression-statement-sequence.js";
3
+ import { tryExpandVariableDeclarationSequenceInitializers } from "./expand-variable-declaration-sequence.js";
4
+ const require = createRequire(import.meta.url);
5
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
6
+ const traverse = require("@babel/traverse").default;
7
+ export const expandSequenceExpressionsTransform = {
8
+ id: "expand-sequence-expressions",
9
+ description: "Expands comma operator sequences in statements and variable initializers",
10
+ scope: "file",
11
+ parallelizable: true,
12
+ transform(context) {
13
+ const { projectGraph } = context;
14
+ let nodesVisited = 0;
15
+ let transformationsApplied = 0;
16
+ for (const [, fileInfo] of projectGraph.files) {
17
+ traverse(fileInfo.ast, {
18
+ ExpressionStatement(path) {
19
+ nodesVisited++;
20
+ if (!tryExpandExpressionStatementSequence(path))
21
+ return;
22
+ transformationsApplied += 1;
23
+ },
24
+ VariableDeclaration(path) {
25
+ nodesVisited++;
26
+ if (!tryExpandVariableDeclarationSequenceInitializers(path))
27
+ return;
28
+ transformationsApplied += 1;
29
+ },
30
+ });
31
+ }
32
+ return Promise.resolve({ nodesVisited, transformationsApplied });
33
+ },
34
+ };
@@ -0,0 +1,3 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { VariableDeclaration } from "@babel/types";
3
+ export declare const tryExpandVariableDeclarationSequenceInitializers: (path: NodePath<VariableDeclaration>) => boolean;
@@ -0,0 +1,93 @@
1
+ import { createRequire } from "node:module";
2
+ const require = createRequire(import.meta.url);
3
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
4
+ const t = require("@babel/types");
5
+ const createExpressionStatement = (expression) => {
6
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
7
+ return t.expressionStatement(
8
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
9
+ t.cloneNode(expression, true));
10
+ };
11
+ const createVariableDeclarationStatement = (kind, declarator) => {
12
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
13
+ return t.variableDeclaration(kind, [declarator]);
14
+ };
15
+ const createVariableDeclarator = (id, init) => {
16
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
17
+ return t.variableDeclarator(
18
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
19
+ t.cloneNode(id, true), init
20
+ ? // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
21
+ t.cloneNode(init, true)
22
+ : undefined);
23
+ };
24
+ const isSupportedStatementContainer = (path) => {
25
+ const parentPath = path.parentPath;
26
+ if (!parentPath)
27
+ return false;
28
+ return (parentPath.isProgram() ||
29
+ parentPath.isBlockStatement() ||
30
+ parentPath.isSwitchCase());
31
+ };
32
+ const isDirectiveProloguePosition = (path) => {
33
+ const parentPath = path.parentPath;
34
+ if (!parentPath)
35
+ return false;
36
+ if (!parentPath.isProgram() && !parentPath.isBlockStatement())
37
+ return false;
38
+ const siblings = parentPath.get("body");
39
+ if (!Array.isArray(siblings))
40
+ return false;
41
+ const index = siblings.findIndex((sibling) => sibling.node === path.node);
42
+ if (index === -1)
43
+ return false;
44
+ for (let bodyIndex = 0; bodyIndex < index; bodyIndex++) {
45
+ const sibling = siblings[bodyIndex];
46
+ if (!sibling)
47
+ return false;
48
+ if (!sibling.isExpressionStatement())
49
+ return false;
50
+ if (sibling.node.expression.type !== "StringLiteral")
51
+ return false;
52
+ }
53
+ return true;
54
+ };
55
+ export const tryExpandVariableDeclarationSequenceInitializers = (path) => {
56
+ if (!isSupportedStatementContainer(path))
57
+ return false;
58
+ if (path.node.kind !== "var" && path.node.declarations.length > 1) {
59
+ return false;
60
+ }
61
+ if (isDirectiveProloguePosition(path)) {
62
+ const wouldInsertStringLiteral = path.node.declarations.some((declarator) => declarator.init?.type === "SequenceExpression" &&
63
+ declarator.init.expressions[0]?.type === "StringLiteral");
64
+ if (wouldInsertStringLiteral)
65
+ return false;
66
+ }
67
+ const hasAnySequence = path.node.declarations.some((declarator) => declarator.init?.type === "SequenceExpression" &&
68
+ declarator.init.expressions.length >= 2);
69
+ if (!hasAnySequence)
70
+ return false;
71
+ const kind = path.node.kind;
72
+ const statements = [];
73
+ for (const declarator of path.node.declarations) {
74
+ const init = declarator.init;
75
+ if (init?.type !== "SequenceExpression") {
76
+ statements.push(createVariableDeclarationStatement(kind, declarator));
77
+ continue;
78
+ }
79
+ const leadingExpressions = init.expressions.slice(0, -1);
80
+ const lastExpression = init.expressions.at(-1);
81
+ if (!lastExpression) {
82
+ statements.push(createVariableDeclarationStatement(kind, declarator));
83
+ continue;
84
+ }
85
+ for (const expression of leadingExpressions) {
86
+ statements.push(createExpressionStatement(expression));
87
+ }
88
+ const newDeclarator = createVariableDeclarator(declarator.id, lastExpression);
89
+ statements.push(createVariableDeclarationStatement(kind, newDeclarator));
90
+ }
91
+ path.replaceWithMultiple(statements);
92
+ return true;
93
+ };
@@ -0,0 +1,3 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { ExpressionStatement } from "@babel/types";
3
+ export declare const tryExpandExpressionStatementSequence: (path: NodePath<ExpressionStatement>) => boolean;
@@ -0,0 +1,55 @@
1
+ import { createRequire } from "node:module";
2
+ const require = createRequire(import.meta.url);
3
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
4
+ const t = require("@babel/types");
5
+ const isSupportedStatementContainer = (path) => {
6
+ const parentPath = path.parentPath;
7
+ if (!parentPath)
8
+ return false;
9
+ return (parentPath.isProgram() ||
10
+ parentPath.isBlockStatement() ||
11
+ parentPath.isSwitchCase());
12
+ };
13
+ const isDirectiveProloguePosition = (path) => {
14
+ const parentPath = path.parentPath;
15
+ if (!parentPath.isProgram() && !parentPath.isBlockStatement())
16
+ return false;
17
+ const siblings = parentPath.get("body");
18
+ if (!Array.isArray(siblings))
19
+ return false;
20
+ const index = siblings.findIndex((sibling) => sibling.node === path.node);
21
+ if (index === -1)
22
+ return false;
23
+ for (let bodyIndex = 0; bodyIndex < index; bodyIndex++) {
24
+ const sibling = siblings[bodyIndex];
25
+ if (!sibling)
26
+ return false;
27
+ if (!sibling.isExpressionStatement())
28
+ return false;
29
+ if (sibling.node.expression.type !== "StringLiteral")
30
+ return false;
31
+ }
32
+ return true;
33
+ };
34
+ export const tryExpandExpressionStatementSequence = (path) => {
35
+ if (!isSupportedStatementContainer(path))
36
+ return false;
37
+ const expression = path.node.expression;
38
+ if (expression.type !== "SequenceExpression")
39
+ return false;
40
+ if (expression.expressions.length < 2)
41
+ return false;
42
+ const firstExpression = expression.expressions[0];
43
+ if (firstExpression?.type === "StringLiteral" &&
44
+ isDirectiveProloguePosition(path)) {
45
+ return false;
46
+ }
47
+ const statements = expression.expressions.map((expr) => {
48
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
49
+ return t.expressionStatement(
50
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
51
+ t.cloneNode(expr, true));
52
+ });
53
+ path.replaceWithMultiple(statements);
54
+ return true;
55
+ };
@@ -0,0 +1,3 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { ReturnStatement } from "@babel/types";
3
+ export declare const tryExpandReturnSequence: (path: NodePath<ReturnStatement>) => boolean;
@@ -0,0 +1,86 @@
1
+ import { createRequire } from "node:module";
2
+ const require = createRequire(import.meta.url);
3
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
4
+ const t = require("@babel/types");
5
+ const canReplaceWithMultiple = (path) => {
6
+ return path.parentPath.isBlockStatement() || path.parentPath.isSwitchCase();
7
+ };
8
+ const canWrapInBlock = (path) => {
9
+ const parentPath = path.parentPath;
10
+ if (parentPath.isIfStatement() &&
11
+ (path.key === "consequent" || path.key === "alternate")) {
12
+ return true;
13
+ }
14
+ if ((parentPath.isForStatement() ||
15
+ parentPath.isForInStatement() ||
16
+ parentPath.isForOfStatement() ||
17
+ parentPath.isWhileStatement() ||
18
+ parentPath.isDoWhileStatement() ||
19
+ parentPath.isWithStatement()) &&
20
+ path.key === "body") {
21
+ return true;
22
+ }
23
+ return false;
24
+ };
25
+ const replaceReturnWithStatements = (path, statements) => {
26
+ if (canReplaceWithMultiple(path)) {
27
+ path.replaceWithMultiple(statements);
28
+ return true;
29
+ }
30
+ if (canWrapInBlock(path)) {
31
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
32
+ path.replaceWith(t.blockStatement(statements));
33
+ return true;
34
+ }
35
+ return false;
36
+ };
37
+ const isDirectiveProloguePosition = (path) => {
38
+ if (!path.parentPath.isBlockStatement())
39
+ return false;
40
+ const siblings = path.parentPath.get("body");
41
+ if (!Array.isArray(siblings))
42
+ return false;
43
+ const index = siblings.findIndex((sibling) => sibling.node === path.node);
44
+ if (index === -1)
45
+ return false;
46
+ for (let bodyIndex = 0; bodyIndex < index; bodyIndex++) {
47
+ const sibling = siblings[bodyIndex];
48
+ if (!sibling)
49
+ return false;
50
+ if (!sibling.isExpressionStatement())
51
+ return false;
52
+ if (sibling.node.expression.type !== "StringLiteral")
53
+ return false;
54
+ }
55
+ return true;
56
+ };
57
+ export const tryExpandReturnSequence = (path) => {
58
+ if (!canReplaceWithMultiple(path) && !canWrapInBlock(path))
59
+ return false;
60
+ const argument = path.node.argument;
61
+ if (!argument)
62
+ return false;
63
+ if (argument.type !== "SequenceExpression")
64
+ return false;
65
+ if (argument.expressions.length < 2)
66
+ return false;
67
+ const leadingExpressions = argument.expressions.slice(0, -1);
68
+ const lastExpression = argument.expressions.at(-1);
69
+ if (!lastExpression)
70
+ return false;
71
+ const firstExpression = leadingExpressions[0];
72
+ if (firstExpression?.type === "StringLiteral" &&
73
+ isDirectiveProloguePosition(path)) {
74
+ return false;
75
+ }
76
+ const statements = leadingExpressions.map((expression) => {
77
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
78
+ return t.expressionStatement(
79
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
80
+ t.cloneNode(expression, true));
81
+ });
82
+ statements.push(
83
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
84
+ t.returnStatement(t.cloneNode(lastExpression, true)));
85
+ return replaceReturnWithStatements(path, statements);
86
+ };
@@ -0,0 +1,2 @@
1
+ import type { Transform } from "../../core/types.js";
2
+ export declare const expandSequenceExpressionsV2Transform: Transform;
@@ -0,0 +1,41 @@
1
+ import { createRequire } from "node:module";
2
+ import { tryExpandExpressionStatementSequence } from "./expand-expression-statement-sequence.js";
3
+ import { tryExpandReturnSequence } from "./expand-return-sequence.js";
4
+ import { tryExpandVariableDeclarationSequenceInitializers } from "./expand-variable-declaration-sequence.js";
5
+ const require = createRequire(import.meta.url);
6
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
7
+ const traverse = require("@babel/traverse").default;
8
+ export const expandSequenceExpressionsV2Transform = {
9
+ id: "expand-sequence-expressions-v2",
10
+ description: "Expands comma operator sequences in returns, statements, and variable initializers",
11
+ scope: "file",
12
+ parallelizable: true,
13
+ transform(context) {
14
+ const { projectGraph } = context;
15
+ let nodesVisited = 0;
16
+ let transformationsApplied = 0;
17
+ for (const [, fileInfo] of projectGraph.files) {
18
+ traverse(fileInfo.ast, {
19
+ ReturnStatement(path) {
20
+ nodesVisited++;
21
+ if (!tryExpandReturnSequence(path))
22
+ return;
23
+ transformationsApplied += 1;
24
+ },
25
+ ExpressionStatement(path) {
26
+ nodesVisited++;
27
+ if (!tryExpandExpressionStatementSequence(path))
28
+ return;
29
+ transformationsApplied += 1;
30
+ },
31
+ VariableDeclaration(path) {
32
+ nodesVisited++;
33
+ if (!tryExpandVariableDeclarationSequenceInitializers(path))
34
+ return;
35
+ transformationsApplied += 1;
36
+ },
37
+ });
38
+ }
39
+ return Promise.resolve({ nodesVisited, transformationsApplied });
40
+ },
41
+ };
@@ -0,0 +1,3 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { VariableDeclaration } from "@babel/types";
3
+ export declare const tryExpandVariableDeclarationSequenceInitializers: (path: NodePath<VariableDeclaration>) => boolean;