@openrewrite/rewrite 8.70.0-20251222-101414 → 8.70.0-20251222-213047

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 (149) hide show
  1. package/dist/index.js +8 -8
  2. package/dist/index.js.map +1 -1
  3. package/dist/java/formatting-utils.d.ts +1 -1
  4. package/dist/java/formatting-utils.js +4 -4
  5. package/dist/java/formatting-utils.js.map +1 -1
  6. package/dist/java/markers.js +8 -8
  7. package/dist/java/markers.js.map +1 -1
  8. package/dist/java/type-visitor.d.ts +0 -3
  9. package/dist/java/type-visitor.d.ts.map +1 -1
  10. package/dist/java/type-visitor.js +67 -68
  11. package/dist/java/type-visitor.js.map +1 -1
  12. package/dist/java/visitor.d.ts +3 -3
  13. package/dist/java/visitor.d.ts.map +1 -1
  14. package/dist/java/visitor.js +5 -5
  15. package/dist/java/visitor.js.map +1 -1
  16. package/dist/javascript/assertions.js +4 -3
  17. package/dist/javascript/assertions.js.map +1 -1
  18. package/dist/javascript/cleanup/use-object-property-shorthand.js +3 -3
  19. package/dist/javascript/cleanup/use-object-property-shorthand.js.map +1 -1
  20. package/dist/javascript/format/format.d.ts +28 -7
  21. package/dist/javascript/format/format.d.ts.map +1 -1
  22. package/dist/javascript/format/format.js +149 -158
  23. package/dist/javascript/format/format.js.map +1 -1
  24. package/dist/javascript/format/minimum-viable-spacing-visitor.js +23 -23
  25. package/dist/javascript/format/minimum-viable-spacing-visitor.js.map +1 -1
  26. package/dist/javascript/format/prettier-format.d.ts.map +1 -1
  27. package/dist/javascript/format/prettier-format.js +8 -11
  28. package/dist/javascript/format/prettier-format.js.map +1 -1
  29. package/dist/javascript/format/tabs-and-indents-visitor.js +7 -7
  30. package/dist/javascript/format/tabs-and-indents-visitor.js.map +1 -1
  31. package/dist/javascript/format/whitespace-reconciler.d.ts.map +1 -1
  32. package/dist/javascript/format/whitespace-reconciler.js +12 -11
  33. package/dist/javascript/format/whitespace-reconciler.js.map +1 -1
  34. package/dist/javascript/markers.d.ts.map +1 -1
  35. package/dist/javascript/markers.js +6 -5
  36. package/dist/javascript/markers.js.map +1 -1
  37. package/dist/javascript/migrate/es6/modernize-octal-escape-sequences.js +2 -2
  38. package/dist/javascript/migrate/es6/modernize-octal-escape-sequences.js.map +1 -1
  39. package/dist/javascript/migrate/es6/modernize-octal-literals.js +2 -2
  40. package/dist/javascript/migrate/es6/modernize-octal-literals.js.map +1 -1
  41. package/dist/javascript/migrate/es6/remove-duplicate-object-keys.js +2 -2
  42. package/dist/javascript/migrate/es6/remove-duplicate-object-keys.js.map +1 -1
  43. package/dist/javascript/migrate/typescript/export-assignment-to-export-default.js +2 -2
  44. package/dist/javascript/migrate/typescript/export-assignment-to-export-default.js.map +1 -1
  45. package/dist/javascript/node-resolution-result.js +3 -9
  46. package/dist/javascript/node-resolution-result.js.map +1 -1
  47. package/dist/javascript/parser.d.ts.map +1 -1
  48. package/dist/javascript/parser.js +35 -35
  49. package/dist/javascript/parser.js.map +1 -1
  50. package/dist/javascript/project-parser.d.ts.map +1 -1
  51. package/dist/javascript/project-parser.js +6 -5
  52. package/dist/javascript/project-parser.js.map +1 -1
  53. package/dist/javascript/recipes/auto-format.d.ts +7 -23
  54. package/dist/javascript/recipes/auto-format.d.ts.map +1 -1
  55. package/dist/javascript/recipes/auto-format.js +8 -54
  56. package/dist/javascript/recipes/auto-format.js.map +1 -1
  57. package/dist/javascript/recipes/change-import.js +8 -8
  58. package/dist/javascript/recipes/change-import.js.map +1 -1
  59. package/dist/javascript/recipes/order-imports.js +4 -4
  60. package/dist/javascript/recipes/order-imports.js.map +1 -1
  61. package/dist/javascript/templating/engine.js +2 -2
  62. package/dist/javascript/templating/engine.js.map +1 -1
  63. package/dist/javascript/templating/index.d.ts +1 -0
  64. package/dist/javascript/templating/index.d.ts.map +1 -1
  65. package/dist/javascript/templating/index.js +4 -1
  66. package/dist/javascript/templating/index.js.map +1 -1
  67. package/dist/javascript/templating/placeholder-replacement.js +16 -16
  68. package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
  69. package/dist/javascript/type-mapping.d.ts.map +1 -1
  70. package/dist/javascript/type-mapping.js +49 -58
  71. package/dist/javascript/type-mapping.js.map +1 -1
  72. package/dist/javascript/visitor.d.ts +3 -3
  73. package/dist/javascript/visitor.d.ts.map +1 -1
  74. package/dist/javascript/visitor.js +3 -3
  75. package/dist/javascript/visitor.js.map +1 -1
  76. package/dist/json/rpc.js +48 -49
  77. package/dist/json/rpc.js.map +1 -1
  78. package/dist/json/visitor.d.ts +3 -3
  79. package/dist/json/visitor.d.ts.map +1 -1
  80. package/dist/json/visitor.js +3 -3
  81. package/dist/json/visitor.js.map +1 -1
  82. package/dist/parse-error.js +11 -11
  83. package/dist/parse-error.js.map +1 -1
  84. package/dist/reference.d.ts.map +1 -1
  85. package/dist/reference.js +0 -3
  86. package/dist/reference.js.map +1 -1
  87. package/dist/rpc/index.js +42 -42
  88. package/dist/rpc/index.js.map +1 -1
  89. package/dist/rpc/queue.d.ts.map +1 -1
  90. package/dist/rpc/queue.js +6 -8
  91. package/dist/rpc/queue.js.map +1 -1
  92. package/dist/rpc/request/install-recipes.js +1 -1
  93. package/dist/rpc/request/install-recipes.js.map +1 -1
  94. package/dist/rpc/request/parse-project.d.ts.map +1 -1
  95. package/dist/rpc/request/parse-project.js +12 -32
  96. package/dist/rpc/request/parse-project.js.map +1 -1
  97. package/dist/rpc/request/parse.d.ts.map +1 -1
  98. package/dist/rpc/request/parse.js +1 -4
  99. package/dist/rpc/request/parse.js.map +1 -1
  100. package/dist/text/rpc.js +18 -18
  101. package/dist/text/rpc.js.map +1 -1
  102. package/dist/version.txt +1 -1
  103. package/dist/visitor.d.ts +6 -4
  104. package/dist/visitor.d.ts.map +1 -1
  105. package/dist/visitor.js +19 -17
  106. package/dist/visitor.js.map +1 -1
  107. package/dist/yaml/rpc.js +65 -65
  108. package/dist/yaml/rpc.js.map +1 -1
  109. package/package.json +2 -2
  110. package/src/index.ts +8 -8
  111. package/src/java/formatting-utils.ts +2 -2
  112. package/src/java/markers.ts +8 -8
  113. package/src/java/type-visitor.ts +59 -68
  114. package/src/java/visitor.ts +8 -8
  115. package/src/javascript/assertions.ts +3 -3
  116. package/src/javascript/cleanup/use-object-property-shorthand.ts +1 -1
  117. package/src/javascript/format/format.ts +109 -107
  118. package/src/javascript/format/minimum-viable-spacing-visitor.ts +1 -1
  119. package/src/javascript/format/prettier-format.ts +9 -12
  120. package/src/javascript/format/tabs-and-indents-visitor.ts +1 -1
  121. package/src/javascript/format/whitespace-reconciler.ts +12 -11
  122. package/src/javascript/markers.ts +6 -6
  123. package/src/javascript/migrate/es6/modernize-octal-escape-sequences.ts +1 -1
  124. package/src/javascript/migrate/es6/modernize-octal-literals.ts +1 -1
  125. package/src/javascript/migrate/es6/remove-duplicate-object-keys.ts +1 -1
  126. package/src/javascript/migrate/typescript/export-assignment-to-export-default.ts +1 -1
  127. package/src/javascript/node-resolution-result.ts +8 -14
  128. package/src/javascript/parser.ts +5 -4
  129. package/src/javascript/project-parser.ts +4 -4
  130. package/src/javascript/recipes/auto-format.ts +9 -71
  131. package/src/javascript/recipes/change-import.ts +4 -4
  132. package/src/javascript/recipes/order-imports.ts +1 -1
  133. package/src/javascript/templating/engine.ts +1 -1
  134. package/src/javascript/templating/index.ts +5 -0
  135. package/src/javascript/templating/placeholder-replacement.ts +2 -2
  136. package/src/javascript/type-mapping.ts +28 -34
  137. package/src/javascript/visitor.ts +6 -6
  138. package/src/json/rpc.ts +51 -52
  139. package/src/json/visitor.ts +6 -6
  140. package/src/parse-error.ts +11 -11
  141. package/src/reference.ts +0 -5
  142. package/src/rpc/index.ts +43 -43
  143. package/src/rpc/queue.ts +6 -8
  144. package/src/rpc/request/install-recipes.ts +1 -1
  145. package/src/rpc/request/parse-project.ts +20 -32
  146. package/src/rpc/request/parse.ts +1 -4
  147. package/src/text/rpc.ts +18 -18
  148. package/src/visitor.ts +29 -30
  149. package/src/yaml/rpc.ts +80 -81
@@ -17,7 +17,7 @@ import {findMarker, Marker, Markers} from "../markers";
17
17
  import {randomId, UUID} from "../uuid";
18
18
  import {asRef} from "../reference";
19
19
  import {RpcCodecs, RpcReceiveQueue, RpcSendQueue} from "../rpc";
20
- import {castDraft} from "immer";
20
+ import {castDraft} from "mutative";
21
21
  import {updateIfChanged} from "../util";
22
22
  import * as semver from "semver";
23
23
  import * as fsp from "fs/promises";
@@ -295,7 +295,7 @@ export function createNodeResolutionResultMarker(
295
295
  function extractPackageInfo(pkgPath: string): { name: string; version?: string } {
296
296
  // For nested packages, we want the last package name
297
297
  const nodeModulesIndex = pkgPath.lastIndexOf('node_modules/');
298
- if (nodeModulesIndex === -1) return { name: pkgPath };
298
+ if (nodeModulesIndex === -1) return {name: pkgPath};
299
299
 
300
300
  let nameWithVersion = pkgPath.slice(nodeModulesIndex + 'node_modules/'.length);
301
301
 
@@ -320,7 +320,7 @@ export function createNodeResolutionResultMarker(
320
320
  }
321
321
  }
322
322
 
323
- return { name: nameWithVersion };
323
+ return {name: nameWithVersion};
324
324
  }
325
325
 
326
326
  /**
@@ -347,7 +347,7 @@ export function createNodeResolutionResultMarker(
347
347
  optionalDependencies: undefined,
348
348
  engines: normalizeEngines(pkgEntry?.engines),
349
349
  license: normalizeLicense(pkgEntry?.license),
350
- });
350
+ }) as ResolvedDependency;
351
351
  resolvedDependencyCache.set(key, resolved);
352
352
 
353
353
  // Maintain name index for O(1) lookup during semver fallback
@@ -470,7 +470,7 @@ export function createNodeResolutionResultMarker(
470
470
  name,
471
471
  versionConstraint,
472
472
  resolved,
473
- });
473
+ }) as Dependency;
474
474
  dependencyCache.set(key, dep);
475
475
  }
476
476
  return dep;
@@ -514,7 +514,7 @@ export function createNodeResolutionResultMarker(
514
514
  const packages = lockContent.packages;
515
515
 
516
516
  // First pass: Create all ResolvedDependency placeholders and build path map
517
- const packageInfos: Array<{path: string; name: string; version: string; entry: PackageLockEntry}> = [];
517
+ const packageInfos: Array<{ path: string; name: string; version: string; entry: PackageLockEntry }> = [];
518
518
  for (const [pkgPath, pkgEntry] of Object.entries(packages)) {
519
519
  // Skip the root package (empty string key)
520
520
  if (pkgPath === '') continue;
@@ -535,7 +535,7 @@ export function createNodeResolutionResultMarker(
535
535
  // Note: Using castDraft here is safe because all objects are created within this
536
536
  // parsing context and haven't been returned to callers yet. The objects in
537
537
  // resolvedDependencyCache are plain JS objects marked with asRef() for RPC
538
- // reference deduplication, not frozen Immer drafts.
538
+ // reference deduplication.
539
539
  for (const {path: pkgPath, name, version, entry} of packageInfos) {
540
540
  const key = `${name}@${version}`;
541
541
  const resolved = resolvedDependencyCache.get(key);
@@ -590,7 +590,7 @@ export function createNodeResolutionResultMarker(
590
590
  packageManager,
591
591
  engines: packageJsonContent.engines,
592
592
  npmrcConfigs,
593
- };
593
+ } as NodeResolutionResult;
594
594
  }
595
595
 
596
596
  /**
@@ -817,7 +817,6 @@ export namespace NodeResolutionResultQueries {
817
817
  RpcCodecs.registerCodec(NpmrcKind, {
818
818
  async rpcReceive(before: Npmrc, q: RpcReceiveQueue): Promise<Npmrc> {
819
819
  return updateIfChanged(before, {
820
- kind: NpmrcKind,
821
820
  scope: await q.receive(before.scope),
822
821
  properties: await q.receive(before.properties),
823
822
  });
@@ -835,7 +834,6 @@ RpcCodecs.registerCodec(NpmrcKind, {
835
834
  RpcCodecs.registerCodec(DependencyKind, {
836
835
  async rpcReceive(before: Dependency, q: RpcReceiveQueue): Promise<Dependency> {
837
836
  return updateIfChanged(before, {
838
- kind: DependencyKind,
839
837
  name: await q.receive(before.name),
840
838
  versionConstraint: await q.receive(before.versionConstraint),
841
839
  resolved: await q.receive(before.resolved),
@@ -855,7 +853,6 @@ RpcCodecs.registerCodec(DependencyKind, {
855
853
  RpcCodecs.registerCodec(ResolvedDependencyKind, {
856
854
  async rpcReceive(before: ResolvedDependency, q: RpcReceiveQueue): Promise<ResolvedDependency> {
857
855
  return updateIfChanged(before, {
858
- kind: ResolvedDependencyKind,
859
856
  name: await q.receive(before.name),
860
857
  version: await q.receive(before.version),
861
858
  dependencies: (await q.receiveList(before.dependencies)) || undefined,
@@ -886,9 +883,6 @@ RpcCodecs.registerCodec(ResolvedDependencyKind, {
886
883
  /**
887
884
  * Register RPC codec for NodeResolutionResult marker.
888
885
  * This handles serialization/deserialization for communication between JS and Java.
889
- * Note: We avoid Immer here because the dependency graph can contain cycles
890
- * (e.g., Dependency -> ResolvedDependency -> Dependency[]), and Immer's proxies
891
- * don't handle cyclic structures correctly.
892
886
  */
893
887
  RpcCodecs.registerCodec(NodeResolutionResultKind, {
894
888
  async rpcReceive(before: NodeResolutionResult, q: RpcReceiveQueue): Promise<NodeResolutionResult> {
@@ -30,7 +30,7 @@ import {
30
30
  VariableDeclarator,
31
31
  } from '../java';
32
32
  import {DelegatedYield, FunctionDeclaration, Generator, JS, JSX, NonNullAssertion, Optional, Spread} from '.';
33
- import {emptyMarkers, markers, Markers, MarkersKind, ParseExceptionResult} from "../markers";
33
+ import {emptyMarkers, markers, Markers, MarkersKind, ParseExceptionResult, replaceMarkerByKind} from "../markers";
34
34
  import {NamedStyles} from "../style";
35
35
  import {Parser, ParserInput, parserInputFile, parserInputRead, ParserOptions, Parsers, SourcePath} from "../parser";
36
36
  import {randomId} from "../uuid";
@@ -47,10 +47,11 @@ import {
47
47
  TextSpan
48
48
  } from "./parser-utils";
49
49
  import {JavaScriptTypeMapping} from "./type-mapping";
50
- import {produce} from "immer";
50
+ import {create as produce} from "mutative";
51
51
  import ComputedPropertyName = JS.ComputedPropertyName;
52
52
  import Attribute = JSX.Attribute;
53
53
  import SpreadAttribute = JSX.SpreadAttribute;
54
+
54
55
  export interface JavaScriptParserOptions extends ParserOptions {
55
56
  styles?: NamedStyles[],
56
57
  sourceFileCache?: Map<string, ts.SourceFile>,
@@ -168,7 +169,7 @@ export class JavaScriptParser extends Parser {
168
169
  if (this.styles) {
169
170
  const styles = this.styles;
170
171
  return produce(result, draft => {
171
- draft.markers.markers = draft.markers.markers.concat(styles);
172
+ draft.markers = styles.reduce((m, s) => replaceMarkerByKind(m, s), draft.markers);
172
173
  });
173
174
  }
174
175
  return result;
@@ -359,7 +360,7 @@ export class JavaScriptParser extends Parser {
359
360
  .visit(sourceFile) as SourceFile,
360
361
  draft => {
361
362
  if (this.styles) {
362
- draft.markers.markers = draft.markers.markers.concat(this.styles);
363
+ draft.markers = this.styles.reduce((m, s) => replaceMarkerByKind(m, s), draft.markers);
363
364
  }
364
365
  });
365
366
  } catch (error) {
@@ -18,12 +18,12 @@ import * as fs from "fs";
18
18
  import * as fsp from "fs/promises";
19
19
  import {spawnSync} from "child_process";
20
20
  import picomatch from "picomatch";
21
- import {produce} from "immer";
21
+ import {create as produce} from "mutative";
22
22
  import {SourceFile} from "../tree";
23
23
  import {Parsers} from "../parser";
24
24
  import {PrettierConfigLoader} from "./format/prettier-config-loader";
25
25
  import {ExecutionContext} from "../execution";
26
- import {Marker} from "../markers";
26
+ import {Marker, replaceMarkerByKind} from "../markers";
27
27
 
28
28
  // Lock file names defined here to avoid circular dependency with package-manager.ts
29
29
  // These must be kept in sync with the definitions in package-manager.ts
@@ -303,7 +303,7 @@ export class ProjectParser {
303
303
  );
304
304
  if (prettierMarker) {
305
305
  yield produce(sf, draft => {
306
- draft.markers.markers = draft.markers.markers.concat([prettierMarker]);
306
+ draft.markers = replaceMarkerByKind(draft.markers, prettierMarker);
307
307
  });
308
308
  } else {
309
309
  yield sf;
@@ -341,7 +341,7 @@ export class ProjectParser {
341
341
  // Yield all files with the Autodetect marker
342
342
  for (const sf of parsedFiles) {
343
343
  yield produce(sf, draft => {
344
- draft.markers.markers = draft.markers.markers.concat([autodetectMarker]);
344
+ draft.markers = replaceMarkerByKind(draft.markers, autodetectMarker);
345
345
  });
346
346
  }
347
347
  }
@@ -14,93 +14,31 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import {ScanningRecipe} from "../../recipe";
17
+ import {Recipe} from "../../recipe";
18
18
  import {TreeVisitor} from "../../visitor";
19
19
  import {ExecutionContext} from "../../execution";
20
20
  import {AutoformatVisitor} from "../format";
21
- import {Autodetect, Detector} from "../autodetect";
22
- import {JavaScriptVisitor} from "../visitor";
23
- import {JS} from "../tree";
24
- import {J} from "../../java";
25
- import {findMarker, MarkersKind} from "../../markers";
26
- import {StyleKind} from "../style";
27
- import {NamedStyles} from "../../style";
28
-
29
- /**
30
- * Accumulator for the AutoFormat scanning recipe.
31
- * Holds the Detector that collects formatting statistics during the scan phase.
32
- */
33
- interface AutoFormatAccumulator {
34
- detector: Detector;
35
- detectedStyles?: Autodetect;
36
- }
37
-
38
- /**
39
- * Checks if a compilation unit already has formatting styles attached.
40
- * Returns true if either PrettierStyle or Autodetect markers are present.
41
- */
42
- function hasFormattingStyles(cu: JS.CompilationUnit): boolean {
43
- // Check for PrettierStyle marker
44
- if (findMarker(cu, StyleKind.PrettierStyle)) {
45
- return true;
46
- }
47
-
48
- // Check for Autodetect marker (NamedStyles with specific name)
49
- const namedStyles = cu.markers.markers.filter(
50
- m => m.kind === MarkersKind.NamedStyles
51
- ) as NamedStyles[];
52
-
53
- return namedStyles.some(s => s.name === "org.openrewrite.javascript.Autodetect");
54
- }
55
21
 
56
22
  /**
57
23
  * Formats JavaScript/TypeScript code using a comprehensive set of formatting rules.
58
24
  *
59
- * This is a scanning recipe that:
60
- * 1. Scans all source files to detect the project's existing formatting style
61
- * 2. Applies consistent formatting based on the detected style
25
+ * This recipe applies formatting based on styles detected at parse time:
26
+ * - If PrettierStyle marker is present, uses Prettier formatting
27
+ * - If Autodetect marker is present, uses auto-detected project style
28
+ * - Otherwise, defaults to IntelliJ IDEA style
62
29
  *
63
30
  * The detected formatting includes:
64
31
  * - Tabs vs spaces preference
65
32
  * - Indent size (2, 4, etc.)
66
33
  * - ES6 import/export brace spacing
67
- *
68
- * If no clear style is detected, defaults to IntelliJ IDEA style.
69
- *
70
- * Files with existing formatting styles (PrettierStyle or Autodetect markers)
71
- * are skipped during style detection since they already have their formatting configured.
72
34
  */
73
- export class AutoFormat extends ScanningRecipe<AutoFormatAccumulator> {
35
+ export class AutoFormat extends Recipe {
74
36
  readonly name = "org.openrewrite.javascript.format.auto-format";
75
37
  readonly displayName = "Auto-format JavaScript/TypeScript code";
76
38
  readonly description = "Format JavaScript and TypeScript code using formatting rules auto-detected from the project's existing code style.";
77
39
 
78
- initialValue(_ctx: ExecutionContext): AutoFormatAccumulator {
79
- return {
80
- detector: Autodetect.detector()
81
- };
82
- }
83
-
84
- async scanner(acc: AutoFormatAccumulator): Promise<TreeVisitor<any, ExecutionContext>> {
85
- return new class extends JavaScriptVisitor<ExecutionContext> {
86
- protected async visitJsCompilationUnit(cu: JS.CompilationUnit, ctx: ExecutionContext): Promise<J | undefined> {
87
- // Skip sampling files that already have formatting styles attached
88
- if (!hasFormattingStyles(cu)) {
89
- await acc.detector.sample(cu);
90
- }
91
- return cu;
92
- }
93
- };
94
- }
95
-
96
- async editorWithData(acc: AutoFormatAccumulator): Promise<TreeVisitor<any, ExecutionContext>> {
97
- // Build detected styles once (lazily on first edit)
98
- if (!acc.detectedStyles) {
99
- acc.detectedStyles = acc.detector.build();
100
- }
101
-
102
- // Pass detected styles to the AutoformatVisitor
103
- // Autodetect is a NamedStyles, so pass it as an array
104
- return new AutoformatVisitor(undefined, [acc.detectedStyles]);
40
+ async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
41
+ // AutoformatVisitor looks up styles from source file markers
42
+ return new AutoformatVisitor();
105
43
  }
106
44
  }
@@ -20,7 +20,7 @@ import {ExecutionContext} from "../../execution";
20
20
  import {JavaScriptVisitor, JS} from "../index";
21
21
  import {maybeAddImport} from "../add-import";
22
22
  import {J, isIdentifier, Type} from "../../java";
23
- import {produce, WritableDraft} from "immer";
23
+ import {create as produce, Draft} from "mutative";
24
24
 
25
25
  /**
26
26
  * Changes an import from one module to another, updating all type attributions.
@@ -206,7 +206,7 @@ export class ChangeImport extends Recipe {
206
206
  this.transformedImport = true;
207
207
  return produce(imp, draft => {
208
208
  if (draft.moduleSpecifier) {
209
- const literal = draft.moduleSpecifier.element as WritableDraft<J.Literal>;
209
+ const literal = draft.moduleSpecifier.element as Draft<J.Literal>;
210
210
  literal.value = newModule;
211
211
  // Update valueSource to preserve quote style
212
212
  const originalSource = literal.valueSource || `"${oldModule}"`;
@@ -217,7 +217,7 @@ export class ChangeImport extends Recipe {
217
217
  if (newMember !== oldMember && oldMember !== 'default' && oldMember !== '*') {
218
218
  const importClause = draft.importClause;
219
219
  if (importClause?.namedBindings?.kind === JS.Kind.NamedImports) {
220
- const namedImports = importClause.namedBindings as WritableDraft<JS.NamedImports>;
220
+ const namedImports = importClause.namedBindings as Draft<JS.NamedImports>;
221
221
  for (const elem of namedImports.elements.elements) {
222
222
  const specifier = elem.element;
223
223
  if (specifier.specifier.kind === J.Kind.Identifier &&
@@ -241,7 +241,7 @@ export class ChangeImport extends Recipe {
241
241
  if (!importClause?.namedBindings) return;
242
242
  if (importClause.namedBindings.kind !== JS.Kind.NamedImports) return;
243
243
 
244
- const namedImports = importClause.namedBindings as WritableDraft<JS.NamedImports>;
244
+ const namedImports = importClause.namedBindings as Draft<JS.NamedImports>;
245
245
  const elements = namedImports.elements.elements;
246
246
  const filteredElements = elements.filter(elem => {
247
247
  const specifier = elem.element;
@@ -19,7 +19,7 @@ import {produceAsync, TreeVisitor} from "../../visitor";
19
19
  import {ExecutionContext} from "../../execution";
20
20
  import {JavaScriptVisitor, JS} from "../index";
21
21
  import {J} from "../../java";
22
- import {Draft, produce} from "immer";
22
+ import {create as produce, Draft} from "mutative";
23
23
  import {SpacesStyle, styleFromSourceFile, StyleKind} from "../style";
24
24
 
25
25
  /**
@@ -16,7 +16,7 @@
16
16
  import {Cursor, isTree, produceAsync, Tree, updateIfChanged} from '../..';
17
17
  import {emptySpace, J, Statement, Type} from '../../java';
18
18
  import {Any, Capture, JavaScriptParser, JavaScriptVisitor, JS} from '..';
19
- import {produce} from 'immer';
19
+ import {create as produce} from 'mutative';
20
20
  import {CaptureMarker, PlaceholderUtils, WRAPPER_FUNCTION_NAME} from './utils';
21
21
  import {CAPTURE_NAME_SYMBOL, CAPTURE_TYPE_SYMBOL, CaptureImpl, CaptureValue, RAW_CODE_SYMBOL, RawCode} from './capture';
22
22
  import {PlaceholderReplacementVisitor} from './placeholder-replacement';
@@ -66,3 +66,8 @@ export {
66
66
  TemplateBuilder,
67
67
  template
68
68
  } from './template';
69
+
70
+ // Export engine utilities (for testing)
71
+ export {
72
+ clearTemplateCache
73
+ } from './engine';
@@ -17,7 +17,7 @@ import {Cursor, isTree} from '../..';
17
17
  import {J} from '../../java';
18
18
  import {JS} from '..';
19
19
  import {JavaScriptVisitor} from '../visitor';
20
- import {produce} from 'immer';
20
+ import {create as produce} from 'mutative';
21
21
  import {PlaceholderUtils} from './utils';
22
22
  import {CaptureImpl, TemplateParamImpl, CaptureValue, CAPTURE_NAME_SYMBOL} from './capture';
23
23
  import {Parameter} from './types';
@@ -260,7 +260,7 @@ export class PlaceholderReplacementVisitor extends JavaScriptVisitor<any> {
260
260
  newElements.push(produce(item, draft => {
261
261
  if (i === 0 && draft.element) {
262
262
  // Merge the placeholder's prefix with the first item's prefix
263
- // Modify prefix directly without nested produce to avoid immer issues
263
+ // Modify prefix directly within the draft
264
264
  draft.element.prefix = this.mergePrefix(draft.element.prefix, element.prefix);
265
265
  }
266
266
  // Keep all other wrapper properties (including markers with Semicolon)
@@ -15,14 +15,8 @@
15
15
  */
16
16
  import ts from "typescript";
17
17
  import {Type} from "../java";
18
- import {immerable} from "immer";
19
18
  import FUNCTION_TYPE_NAME = Type.FUNCTION_TYPE_NAME;
20
19
 
21
- // Helper class to create Type objects that immer won't traverse
22
- class NonDraftableType {
23
- [immerable] = false;
24
- }
25
-
26
20
  export class JavaScriptTypeMapping {
27
21
  // Primary cache: Use type signatures (preferring type.id) as cache keys
28
22
  // TypeScript assigns stable IDs to all types, so we don't need secondary caches
@@ -180,7 +174,7 @@ export class JavaScriptTypeMapping {
180
174
  }
181
175
 
182
176
  // Create the parameterized type wrapper
183
- const parameterized = Object.assign(new NonDraftableType(), {
177
+ const parameterized = {
184
178
  kind: Type.Kind.Parameterized,
185
179
  type: classType,
186
180
  typeParameters: typeParameters,
@@ -188,7 +182,7 @@ export class JavaScriptTypeMapping {
188
182
  toJSON: function () {
189
183
  return Type.signature(this);
190
184
  }
191
- }) as Type.Parameterized;
185
+ } as Type.Parameterized;
192
186
 
193
187
  // Cache the parameterized type
194
188
  this.typeCache.set(signature, parameterized);
@@ -418,7 +412,7 @@ export class JavaScriptTypeMapping {
418
412
  if (ts.isStringLiteral(importDecl.moduleSpecifier)) {
419
413
  const moduleSpecifier = importDecl.moduleSpecifier.text;
420
414
  // Create a Type.Class representing the module
421
- ownerType = Object.assign(new NonDraftableType(), {
415
+ ownerType = {
422
416
  kind: Type.Kind.Class,
423
417
  flags: 0,
424
418
  classKind: Type.Class.Kind.Interface,
@@ -431,7 +425,7 @@ export class JavaScriptTypeMapping {
431
425
  toJSON: function () {
432
426
  return Type.signature(this);
433
427
  }
434
- }) as Type.Class;
428
+ } as Type.Class;
435
429
  }
436
430
  }
437
431
  }
@@ -461,7 +455,7 @@ export class JavaScriptTypeMapping {
461
455
  // Store the module as the owningClass for now
462
456
  // (This is a bit of a hack, but works with the current type system)
463
457
  if (Type.isClass(ownerType)) {
464
- (ownerType as any).owningClass = Object.assign(new NonDraftableType(), {
458
+ (ownerType as any).owningClass = {
465
459
  kind: Type.Kind.Class,
466
460
  flags: 0,
467
461
  classKind: Type.Class.Kind.Interface,
@@ -474,7 +468,7 @@ export class JavaScriptTypeMapping {
474
468
  toJSON: function () {
475
469
  return Type.signature(this);
476
470
  }
477
- }) as Type.Class;
471
+ } as Type.Class;
478
472
  }
479
473
  }
480
474
  }
@@ -485,7 +479,7 @@ export class JavaScriptTypeMapping {
485
479
  }
486
480
 
487
481
  // Create the Type.Variable
488
- const variable = Object.assign(new NonDraftableType(), {
482
+ const variable = {
489
483
  kind: Type.Kind.Variable,
490
484
  name: actualSymbol.getName(),
491
485
  owner: ownerType,
@@ -494,7 +488,7 @@ export class JavaScriptTypeMapping {
494
488
  toJSON: function () {
495
489
  return Type.signature(this);
496
490
  }
497
- }) as Type.Variable;
491
+ } as Type.Variable;
498
492
 
499
493
  return variable;
500
494
  }
@@ -593,7 +587,7 @@ export class JavaScriptTypeMapping {
593
587
  }
594
588
 
595
589
  // Create the Type.Method object
596
- const method = Object.assign(new NonDraftableType(), {
590
+ const method = {
597
591
  kind: Type.Kind.Method,
598
592
  flags: 0, // FIXME - determine flags
599
593
  declaringType: declaringType,
@@ -608,7 +602,7 @@ export class JavaScriptTypeMapping {
608
602
  toJSON: function () {
609
603
  return Type.signature(this);
610
604
  }
611
- }) as Type.Method;
605
+ } as Type.Method;
612
606
 
613
607
  this.methodCache.set(cacheKey, method);
614
608
  return method;
@@ -1062,7 +1056,7 @@ export class JavaScriptTypeMapping {
1062
1056
  }
1063
1057
 
1064
1058
  // Create empty class type shell (no members yet to avoid recursion)
1065
- return Object.assign(new NonDraftableType(), {
1059
+ return {
1066
1060
  kind: Type.Kind.Class,
1067
1061
  flags: 0, // TODO - determine flags
1068
1062
  classKind: classKind,
@@ -1075,7 +1069,7 @@ export class JavaScriptTypeMapping {
1075
1069
  toJSON: function () {
1076
1070
  return Type.signature(this);
1077
1071
  }
1078
- }) as Type.Class;
1072
+ } as Type.Class;
1079
1073
  }
1080
1074
 
1081
1075
  /**
@@ -1191,7 +1185,7 @@ export class JavaScriptTypeMapping {
1191
1185
  } else {
1192
1186
  // Create Type.Variable for fields/properties
1193
1187
  const propType = this.checker.getTypeOfSymbolAtLocation(prop, declaration);
1194
- const variable: Type.Variable = Object.assign(new NonDraftableType(), {
1188
+ const variable: Type.Variable = {
1195
1189
  kind: Type.Kind.Variable,
1196
1190
  name: prop.getName(),
1197
1191
  owner: classType, // Cyclic reference to the containing class (already in cache)
@@ -1200,7 +1194,7 @@ export class JavaScriptTypeMapping {
1200
1194
  toJSON: function () {
1201
1195
  return Type.signature(this);
1202
1196
  }
1203
- }) as Type.Variable;
1197
+ } as Type.Variable;
1204
1198
  classType.members.push(variable);
1205
1199
  }
1206
1200
  }
@@ -1261,10 +1255,10 @@ export class JavaScriptTypeMapping {
1261
1255
  */
1262
1256
  private createUnionType(unionType: ts.UnionType, cacheKey: string | number): Type.Union {
1263
1257
  // Shell-cache FIRST to prevent infinite recursion (before resolving constituent types)
1264
- const union = Object.assign(new NonDraftableType(), {
1258
+ const union = {
1265
1259
  kind: Type.Kind.Union,
1266
1260
  bounds: []
1267
- }) as Type.Union;
1261
+ } as Type.Union;
1268
1262
 
1269
1263
  this.typeCache.set(cacheKey, union);
1270
1264
 
@@ -1286,10 +1280,10 @@ export class JavaScriptTypeMapping {
1286
1280
  */
1287
1281
  private createIntersectionType(intersectionType: ts.IntersectionType, cacheKey: string | number): Type.Intersection {
1288
1282
  // Shell-cache FIRST to prevent infinite recursion (before resolving constituent types)
1289
- const intersection = Object.assign(new NonDraftableType(), {
1283
+ const intersection = {
1290
1284
  kind: Type.Kind.Intersection,
1291
1285
  bounds: []
1292
- }) as Type.Intersection;
1286
+ } as Type.Intersection;
1293
1287
 
1294
1288
  this.typeCache.set(cacheKey, intersection);
1295
1289
 
@@ -1315,12 +1309,12 @@ export class JavaScriptTypeMapping {
1315
1309
  const name = symbol ? symbol.getName() : '?';
1316
1310
 
1317
1311
  // Shell-cache: Create stub, cache it, then populate (prevents cycles)
1318
- const gtv = Object.assign(new NonDraftableType(), {
1312
+ const gtv = {
1319
1313
  kind: Type.Kind.GenericTypeVariable,
1320
1314
  name: name,
1321
1315
  variance: Type.GenericTypeVariable.Variance.Invariant,
1322
1316
  bounds: []
1323
- }) as Type.GenericTypeVariable;
1317
+ } as Type.GenericTypeVariable;
1324
1318
 
1325
1319
  this.typeCache.set(cacheKey, gtv);
1326
1320
 
@@ -1350,7 +1344,7 @@ export class JavaScriptTypeMapping {
1350
1344
  * The shell will be populated later to handle circular references.
1351
1345
  */
1352
1346
  private createEmptyFunctionType(): Type.Class {
1353
- return Object.assign(new NonDraftableType(), {
1347
+ return {
1354
1348
  kind: Type.Kind.Class,
1355
1349
  flags: 0,
1356
1350
  classKind: Type.Class.Kind.Interface,
@@ -1363,7 +1357,7 @@ export class JavaScriptTypeMapping {
1363
1357
  toJSON: function () {
1364
1358
  return Type.signature(this);
1365
1359
  }
1366
- }) as Type.Class;
1360
+ } as Type.Class;
1367
1361
  }
1368
1362
 
1369
1363
  /**
@@ -1394,27 +1388,27 @@ export class JavaScriptTypeMapping {
1394
1388
  const typeParameters: Type[] = [];
1395
1389
 
1396
1390
  // Return type parameter (covariant)
1397
- typeParameters.push(Object.assign(new NonDraftableType(), {
1391
+ typeParameters.push({
1398
1392
  kind: Type.Kind.GenericTypeVariable,
1399
1393
  name: 'R',
1400
1394
  variance: Type.GenericTypeVariable.Variance.Covariant,
1401
1395
  bounds: [returnType]
1402
- }) as Type.GenericTypeVariable);
1396
+ } as Type.GenericTypeVariable);
1403
1397
 
1404
1398
  // Parameter type variables (contravariant)
1405
1399
  parameterTypes.forEach((paramType, index) => {
1406
- typeParameters.push(Object.assign(new NonDraftableType(), {
1400
+ typeParameters.push({
1407
1401
  kind: Type.Kind.GenericTypeVariable,
1408
1402
  name: `P${index + 1}`,
1409
1403
  variance: Type.GenericTypeVariable.Variance.Contravariant,
1410
1404
  bounds: [paramType]
1411
- }) as Type.GenericTypeVariable);
1405
+ } as Type.GenericTypeVariable);
1412
1406
  });
1413
1407
 
1414
1408
  functionClass.typeParameters = typeParameters;
1415
1409
 
1416
1410
  // Create the apply() method
1417
- const applyMethod = Object.assign(new NonDraftableType(), {
1411
+ const applyMethod = {
1418
1412
  kind: Type.Kind.Method,
1419
1413
  flags: 0,
1420
1414
  declaringType: functionClass,
@@ -1429,7 +1423,7 @@ export class JavaScriptTypeMapping {
1429
1423
  toJSON: function () {
1430
1424
  return Type.signature(this);
1431
1425
  }
1432
- }) as Type.Method;
1426
+ } as Type.Method;
1433
1427
 
1434
1428
  // Add the apply method to the function class
1435
1429
  functionClass.methods.push(applyMethod);
@@ -17,9 +17,9 @@
17
17
  */
18
18
  import {mapAsync, updateIfChanged} from "../util";
19
19
  import {Cursor, SourceFile} from "../tree";
20
- import {ValidImmerRecipeReturnType} from "../visitor";
20
+ import {ValidRecipeReturnType} from "../visitor";
21
21
  import {Expression, J, Type, JavaVisitor, NameTree, Statement, TypedTree} from "../java";
22
- import {createDraft, Draft, finishDraft} from "immer";
22
+ import {create, Draft} from "mutative";
23
23
  import {isJavaScript, JS, JSX} from "./tree";
24
24
  import ComputedPropertyName = JS.ComputedPropertyName;
25
25
 
@@ -58,16 +58,16 @@ export class JavaScriptVisitor<P> extends JavaVisitor<P> {
58
58
  before: J2,
59
59
  p: P,
60
60
  recipe?: (draft: Draft<J2>) =>
61
- ValidImmerRecipeReturnType<Draft<J2>> |
62
- PromiseLike<ValidImmerRecipeReturnType<Draft<J2>>>
61
+ ValidRecipeReturnType<Draft<J2>> |
62
+ PromiseLike<ValidRecipeReturnType<Draft<J2>>>
63
63
  ): Promise<J2> {
64
- const draft: Draft<J2> = createDraft(before!);
64
+ const [draft, finishDraft] = create(before!);
65
65
  (draft as Draft<J>).prefix = await this.visitSpace(before!.prefix, p);
66
66
  (draft as Draft<J>).markers = await this.visitMarkers(before!.markers, p);
67
67
  if (recipe) {
68
68
  await recipe(draft);
69
69
  }
70
- return finishDraft(draft) as J2;
70
+ return finishDraft() as J2;
71
71
  }
72
72
 
73
73
  protected async visitAlias(alias: JS.Alias, p: P): Promise<J | undefined> {