@openrewrite/rewrite 8.69.0-20251205-160300 → 8.69.0-20251205-210445

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 (35) hide show
  1. package/dist/cli/cli-utils.d.ts +2 -1
  2. package/dist/cli/cli-utils.d.ts.map +1 -1
  3. package/dist/cli/cli-utils.js +9 -1
  4. package/dist/cli/cli-utils.js.map +1 -1
  5. package/dist/cli/rewrite.d.ts.map +1 -1
  6. package/dist/cli/rewrite.js +65 -6
  7. package/dist/cli/rewrite.js.map +1 -1
  8. package/dist/java/formatting-utils.d.ts +15 -0
  9. package/dist/java/formatting-utils.d.ts.map +1 -1
  10. package/dist/java/formatting-utils.js +32 -0
  11. package/dist/java/formatting-utils.js.map +1 -1
  12. package/dist/java/index.d.ts +1 -0
  13. package/dist/java/index.d.ts.map +1 -1
  14. package/dist/java/index.js +1 -0
  15. package/dist/java/index.js.map +1 -1
  16. package/dist/javascript/format.d.ts +2 -14
  17. package/dist/javascript/format.d.ts.map +1 -1
  18. package/dist/javascript/format.js +62 -215
  19. package/dist/javascript/format.js.map +1 -1
  20. package/dist/javascript/parser.d.ts.map +1 -1
  21. package/dist/javascript/parser.js +3 -1
  22. package/dist/javascript/parser.js.map +1 -1
  23. package/dist/javascript/tabs-and-indents-visitor.d.ts +25 -0
  24. package/dist/javascript/tabs-and-indents-visitor.d.ts.map +1 -0
  25. package/dist/javascript/tabs-and-indents-visitor.js +257 -0
  26. package/dist/javascript/tabs-and-indents-visitor.js.map +1 -0
  27. package/dist/version.txt +1 -1
  28. package/package.json +1 -1
  29. package/src/cli/cli-utils.ts +12 -1
  30. package/src/cli/rewrite.ts +66 -2
  31. package/src/java/formatting-utils.ts +31 -0
  32. package/src/java/index.ts +1 -0
  33. package/src/javascript/format.ts +66 -197
  34. package/src/javascript/parser.ts +3 -1
  35. package/src/javascript/tabs-and-indents-visitor.ts +250 -0
@@ -0,0 +1,25 @@
1
+ import { JavaScriptVisitor } from "./visitor";
2
+ import { J } from "../java";
3
+ import { Cursor, Tree } from "../tree";
4
+ import { TabsAndIndentsStyle } from "./style";
5
+ export declare class TabsAndIndentsVisitor<P> extends JavaScriptVisitor<P> {
6
+ private readonly tabsAndIndentsStyle;
7
+ private stopAfter?;
8
+ private readonly singleIndent;
9
+ constructor(tabsAndIndentsStyle: TabsAndIndentsStyle, stopAfter?: Tree | undefined);
10
+ protected preVisit(tree: J, _p: P): Promise<J | undefined>;
11
+ private setupCursorMessagesForTree;
12
+ private getParentIndentContext;
13
+ private computeMyIndent;
14
+ private computeIndentKind;
15
+ postVisit(tree: J, _p: P): Promise<J | undefined>;
16
+ private normalizeBlockEnd;
17
+ private normalizeJsxTagEnd;
18
+ visitContainer<T extends J>(container: J.Container<T>, p: P): Promise<J.Container<T>>;
19
+ visitLeftPadded<T extends J | J.Space | number | string | boolean>(left: J.LeftPadded<T>, p: P): Promise<J.LeftPadded<T> | undefined>;
20
+ visit<R extends J>(tree: Tree, p: P, parent?: Cursor): Promise<R | undefined>;
21
+ private setupAncestorIndents;
22
+ private isActualJNode;
23
+ private combineIndent;
24
+ }
25
+ //# sourceMappingURL=tabs-and-indents-visitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-and-indents-visitor.d.ts","sourceRoot":"","sources":["../../src/javascript/tabs-and-indents-visitor.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAC,iBAAiB,EAAC,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAS,CAAC,EAAwC,MAAM,SAAS,CAAC;AAEzE,OAAO,EAAC,MAAM,EAAW,IAAI,EAAC,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAC,mBAAmB,EAAC,MAAM,SAAS,CAAC;AAG5C,qBAAa,qBAAqB,CAAC,CAAC,CAAE,SAAQ,iBAAiB,CAAC,CAAC,CAAC;IAGlD,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAAuB,OAAO,CAAC,SAAS,CAAC;IAFzF,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAET,mBAAmB,EAAE,mBAAmB,EAAU,SAAS,CAAC,EAAE,IAAI,YAAA;cAU/E,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAKhE,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,iBAAiB;IAYV,SAAS,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IA0BhE,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,kBAAkB;IAcb,cAAc,CAAC,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAgCrF,eAAe,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAC1E,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EACrB,CAAC,EAAE,CAAC,GACL,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAWjC,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAanF,OAAO,CAAC,oBAAoB;IA8C5B,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;CAIxB"}
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TabsAndIndentsVisitor = void 0;
13
+ /*
14
+ * Copyright 2025 the original author or authors.
15
+ * <p>
16
+ * Licensed under the Moderne Source Available License (the "License");
17
+ * you may not use this file except in compliance with the License.
18
+ * You may obtain a copy of the License at
19
+ * <p>
20
+ * https://docs.moderne.io/licensing/moderne-source-available-license
21
+ * <p>
22
+ * Unless required by applicable law or agreed to in writing, software
23
+ * distributed under the License is distributed on an "AS IS" BASIS,
24
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25
+ * See the License for the specific language governing permissions and
26
+ * limitations under the License.
27
+ */
28
+ const tree_1 = require("./tree");
29
+ const visitor_1 = require("./visitor");
30
+ const java_1 = require("../java");
31
+ const immer_1 = require("immer");
32
+ const tree_2 = require("../tree");
33
+ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
34
+ constructor(tabsAndIndentsStyle, stopAfter) {
35
+ super();
36
+ this.tabsAndIndentsStyle = tabsAndIndentsStyle;
37
+ this.stopAfter = stopAfter;
38
+ if (this.tabsAndIndentsStyle.useTabCharacter) {
39
+ this.singleIndent = "\t";
40
+ }
41
+ else {
42
+ this.singleIndent = " ".repeat(this.tabsAndIndentsStyle.indentSize);
43
+ }
44
+ }
45
+ preVisit(tree, _p) {
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ this.setupCursorMessagesForTree(this.cursor, tree);
48
+ return tree;
49
+ });
50
+ }
51
+ setupCursorMessagesForTree(cursor, tree) {
52
+ const [parentMyIndent, parentIndentKind] = this.getParentIndentContext(cursor);
53
+ const myIndent = this.computeMyIndent(tree, parentMyIndent, parentIndentKind);
54
+ cursor.messages.set("myIndent", myIndent);
55
+ cursor.messages.set("indentKind", this.computeIndentKind(tree));
56
+ }
57
+ getParentIndentContext(cursor) {
58
+ var _a;
59
+ for (let c = cursor.parent; c != null; c = c.parent) {
60
+ const indent = c.messages.get("myIndent");
61
+ if (indent !== undefined) {
62
+ const kind = (_a = c.messages.get("indentKind")) !== null && _a !== void 0 ? _a : 'continuation';
63
+ return [indent, kind];
64
+ }
65
+ }
66
+ return ["", 'continuation'];
67
+ }
68
+ computeMyIndent(tree, parentMyIndent, parentIndentKind) {
69
+ var _a, _b, _c, _d;
70
+ if (tree.kind === java_1.J.Kind.IfElse || parentIndentKind === 'align') {
71
+ return parentMyIndent;
72
+ }
73
+ if (parentIndentKind === 'block') {
74
+ return parentMyIndent + this.singleIndent;
75
+ }
76
+ const hasNewline = ((_b = (_a = tree.prefix) === null || _a === void 0 ? void 0 : _a.whitespace) === null || _b === void 0 ? void 0 : _b.includes("\n")) ||
77
+ ((_d = (_c = tree.prefix) === null || _c === void 0 ? void 0 : _c.comments) === null || _d === void 0 ? void 0 : _d.some(c => c.suffix.includes("\n")));
78
+ return hasNewline ? parentMyIndent + this.singleIndent : parentMyIndent;
79
+ }
80
+ computeIndentKind(tree) {
81
+ switch (tree.kind) {
82
+ case java_1.J.Kind.Block:
83
+ case java_1.J.Kind.Case:
84
+ return 'block';
85
+ case tree_1.JS.Kind.CompilationUnit:
86
+ return 'align';
87
+ default:
88
+ return 'continuation';
89
+ }
90
+ }
91
+ postVisit(tree, _p) {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ var _a, _b, _c;
94
+ if (this.stopAfter != null && (0, tree_2.isScope)(this.stopAfter, tree)) {
95
+ (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.root.messages.set("stop", true);
96
+ }
97
+ const myIndent = this.cursor.messages.get("myIndent");
98
+ if (myIndent === undefined) {
99
+ return tree;
100
+ }
101
+ let result = tree;
102
+ if ((_c = (_b = result.prefix) === null || _b === void 0 ? void 0 : _b.whitespace) === null || _c === void 0 ? void 0 : _c.includes("\n")) {
103
+ result = (0, immer_1.produce)(result, draft => {
104
+ draft.prefix.whitespace = this.combineIndent(draft.prefix.whitespace, myIndent);
105
+ });
106
+ }
107
+ if (result.kind === java_1.J.Kind.Block) {
108
+ result = this.normalizeBlockEnd(result, myIndent);
109
+ }
110
+ else if (result.kind === tree_1.JS.Kind.JsxTag) {
111
+ result = this.normalizeJsxTagEnd(result, myIndent);
112
+ }
113
+ return result;
114
+ });
115
+ }
116
+ normalizeBlockEnd(block, myIndent) {
117
+ const effectiveLastWs = (0, java_1.lastWhitespace)(block.end);
118
+ if (!effectiveLastWs.includes("\n")) {
119
+ return block;
120
+ }
121
+ return (0, immer_1.produce)(block, draft => {
122
+ draft.end = (0, java_1.replaceLastWhitespace)(draft.end, ws => this.combineIndent(ws, myIndent));
123
+ });
124
+ }
125
+ normalizeJsxTagEnd(tag, myIndent) {
126
+ if (!tag.children || tag.children.length === 0) {
127
+ return tag;
128
+ }
129
+ const lastChild = tag.children[tag.children.length - 1];
130
+ if (lastChild.kind !== java_1.J.Kind.Literal || !lastChild.prefix.whitespace.includes("\n")) {
131
+ return tag;
132
+ }
133
+ return (0, immer_1.produce)(tag, draft => {
134
+ const lastChildDraft = draft.children[draft.children.length - 1];
135
+ lastChildDraft.prefix.whitespace = this.combineIndent(lastChildDraft.prefix.whitespace, myIndent);
136
+ });
137
+ }
138
+ visitContainer(container, p) {
139
+ const _super = Object.create(null, {
140
+ visitContainer: { get: () => super.visitContainer }
141
+ });
142
+ return __awaiter(this, void 0, void 0, function* () {
143
+ var _a;
144
+ const parentIndent = (_a = this.cursor.messages.get("myIndent")) !== null && _a !== void 0 ? _a : "";
145
+ const elementsIndent = container.before.whitespace.includes("\n")
146
+ ? parentIndent + this.singleIndent
147
+ : parentIndent;
148
+ const savedMyIndent = this.cursor.messages.get("myIndent");
149
+ this.cursor.messages.set("myIndent", elementsIndent);
150
+ let ret = yield _super.visitContainer.call(this, container, p);
151
+ if (savedMyIndent !== undefined) {
152
+ this.cursor.messages.set("myIndent", savedMyIndent);
153
+ }
154
+ if (ret.before.whitespace.includes("\n")) {
155
+ ret = (0, immer_1.produce)(ret, draft => {
156
+ draft.before.whitespace = this.combineIndent(draft.before.whitespace, elementsIndent);
157
+ });
158
+ }
159
+ if (ret.elements.length > 0) {
160
+ const effectiveLastWs = (0, java_1.lastWhitespace)(ret.elements[ret.elements.length - 1].after);
161
+ if (effectiveLastWs.includes("\n")) {
162
+ ret = (0, immer_1.produce)(ret, draft => {
163
+ const lastDraft = draft.elements[draft.elements.length - 1];
164
+ lastDraft.after = (0, java_1.replaceLastWhitespace)(lastDraft.after, ws => this.combineIndent(ws, parentIndent));
165
+ });
166
+ }
167
+ }
168
+ return ret;
169
+ });
170
+ }
171
+ visitLeftPadded(left, p) {
172
+ const _super = Object.create(null, {
173
+ visitLeftPadded: { get: () => super.visitLeftPadded }
174
+ });
175
+ return __awaiter(this, void 0, void 0, function* () {
176
+ var _a;
177
+ const ret = yield _super.visitLeftPadded.call(this, left, p);
178
+ if (ret === undefined || !ret.before.whitespace.includes("\n")) {
179
+ return ret;
180
+ }
181
+ const parentIndent = (_a = this.cursor.messages.get("myIndent")) !== null && _a !== void 0 ? _a : "";
182
+ return (0, immer_1.produce)(ret, draft => {
183
+ draft.before.whitespace = this.combineIndent(draft.before.whitespace, parentIndent + this.singleIndent);
184
+ });
185
+ });
186
+ }
187
+ visit(tree, p, parent) {
188
+ const _super = Object.create(null, {
189
+ visit: { get: () => super.visit }
190
+ });
191
+ return __awaiter(this, void 0, void 0, function* () {
192
+ var _a;
193
+ if (((_a = this.cursor) === null || _a === void 0 ? void 0 : _a.getNearestMessage("stop")) != null) {
194
+ return tree;
195
+ }
196
+ if (parent) {
197
+ this.cursor = new tree_2.Cursor(tree, parent);
198
+ this.setupAncestorIndents();
199
+ }
200
+ return yield _super.visit.call(this, tree, p);
201
+ });
202
+ }
203
+ setupAncestorIndents() {
204
+ const path = [];
205
+ let anchorCursor;
206
+ let anchorIndent = "";
207
+ for (let c = this.cursor.parent; c; c = c.parent) {
208
+ path.push(c);
209
+ const v = c.value;
210
+ if (this.isActualJNode(v) && !anchorCursor && v.prefix) {
211
+ const ws = (0, java_1.lastWhitespace)(v.prefix);
212
+ const idx = ws.lastIndexOf('\n');
213
+ if (idx !== -1) {
214
+ anchorCursor = c;
215
+ anchorIndent = ws.substring(idx + 1);
216
+ }
217
+ }
218
+ if (v.kind === tree_1.JS.Kind.CompilationUnit) {
219
+ if (!anchorCursor) {
220
+ anchorCursor = c;
221
+ anchorIndent = "";
222
+ }
223
+ break;
224
+ }
225
+ }
226
+ if (path.length === 0)
227
+ return;
228
+ path.reverse();
229
+ for (const c of path) {
230
+ const v = c.value;
231
+ if (!this.isActualJNode(v))
232
+ continue;
233
+ const savedCursor = this.cursor;
234
+ this.cursor = c;
235
+ if (c === anchorCursor) {
236
+ c.messages.set("myIndent", anchorIndent);
237
+ c.messages.set("indentKind", this.computeIndentKind(v));
238
+ }
239
+ else {
240
+ this.setupCursorMessagesForTree(c, v);
241
+ }
242
+ this.cursor = savedCursor;
243
+ }
244
+ }
245
+ isActualJNode(v) {
246
+ return ((0, java_1.isJava)(v) || (0, tree_1.isJavaScript)(v)) &&
247
+ v.kind !== java_1.J.Kind.Container &&
248
+ v.kind !== java_1.J.Kind.LeftPadded &&
249
+ v.kind !== java_1.J.Kind.RightPadded;
250
+ }
251
+ combineIndent(oldWs, newIndent) {
252
+ const lastNewline = oldWs.lastIndexOf("\n");
253
+ return oldWs.substring(0, lastNewline + 1) + newIndent;
254
+ }
255
+ }
256
+ exports.TabsAndIndentsVisitor = TabsAndIndentsVisitor;
257
+ //# sourceMappingURL=tabs-and-indents-visitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-and-indents-visitor.js","sourceRoot":"","sources":["../../src/javascript/tabs-and-indents-visitor.ts"],"names":[],"mappings":"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"}
package/dist/version.txt CHANGED
@@ -1 +1 @@
1
- 8.69.0-20251205-160300
1
+ 8.69.0-20251205-210445
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openrewrite/rewrite",
3
- "version": "8.69.0-20251205-160300",
3
+ "version": "8.69.0-20251205-210445",
4
4
  "license": "Moderne Source Available License",
5
5
  "description": "OpenRewrite JavaScript.",
6
6
  "homepage": "https://github.com/openrewrite/rewrite",
@@ -452,15 +452,20 @@ export function isAcceptedFile(filePath: string): boolean {
452
452
  return false;
453
453
  }
454
454
 
455
+ export type ProgressCallback = (current: number, total: number, filePath: string) => void;
456
+
455
457
  /**
456
458
  * Parse source files using appropriate parsers
457
459
  */
458
460
  export async function parseFiles(
459
461
  filePaths: string[],
460
462
  projectRoot: string,
461
- verbose: boolean = false
463
+ verbose: boolean = false,
464
+ onProgress?: ProgressCallback
462
465
  ): Promise<SourceFile[]> {
463
466
  const parsed: SourceFile[] = [];
467
+ const total = filePaths.length;
468
+ let current = 0;
464
469
 
465
470
  // Group files by type
466
471
  const jsFiles: string[] = [];
@@ -487,6 +492,8 @@ export async function parseFiles(
487
492
  }
488
493
  const jsParser = new JavaScriptParser({relativeTo: projectRoot});
489
494
  for await (const sf of jsParser.parse(...jsFiles)) {
495
+ current++;
496
+ onProgress?.(current, total, sf.sourcePath);
490
497
  parsed.push(sf);
491
498
  }
492
499
  }
@@ -498,6 +505,8 @@ export async function parseFiles(
498
505
  }
499
506
  const pkgParser = new PackageJsonParser({relativeTo: projectRoot});
500
507
  for await (const sf of pkgParser.parse(...packageJsonFiles)) {
508
+ current++;
509
+ onProgress?.(current, total, sf.sourcePath);
501
510
  parsed.push(sf);
502
511
  }
503
512
  }
@@ -509,6 +518,8 @@ export async function parseFiles(
509
518
  }
510
519
  const jsonParser = new JsonParser({relativeTo: projectRoot});
511
520
  for await (const sf of jsonParser.parse(...jsonFiles)) {
521
+ current++;
522
+ onProgress?.(current, total, sf.sourcePath);
512
523
  parsed.push(sf);
513
524
  }
514
525
  }
@@ -34,6 +34,44 @@ import {
34
34
  parseRecipeSpec
35
35
  } from './cli-utils';
36
36
 
37
+ const isTTY = process.stdout.isTTY ?? false;
38
+ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
39
+
40
+ class Spinner {
41
+ private frameIndex = 0;
42
+ private interval: NodeJS.Timeout | null = null;
43
+ private message = '';
44
+
45
+ start(message: string): void {
46
+ if (!isTTY) return;
47
+ this.message = message;
48
+ this.render();
49
+ this.interval = setInterval(() => this.render(), 80);
50
+ }
51
+
52
+ update(message: string): void {
53
+ this.message = message;
54
+ if (!isTTY) return;
55
+ this.render();
56
+ }
57
+
58
+ private render(): void {
59
+ const frame = SPINNER_FRAMES[this.frameIndex];
60
+ this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
61
+ process.stdout.write(`\r\x1b[K${frame} ${this.message}`);
62
+ }
63
+
64
+ stop(): void {
65
+ if (this.interval) {
66
+ clearInterval(this.interval);
67
+ this.interval = null;
68
+ }
69
+ if (isTTY) {
70
+ process.stdout.write('\r\x1b[K');
71
+ }
72
+ }
73
+ }
74
+
37
75
  // Import language modules to register printers
38
76
  import '../text';
39
77
  import '../json';
@@ -127,9 +165,13 @@ async function main() {
127
165
  console.log(`Running recipe: ${recipe.name}`);
128
166
  }
129
167
 
130
- // Discover source files
168
+ const spinner = new Spinner();
131
169
  const projectRoot = process.cwd();
170
+
171
+ // Discover source files
172
+ spinner.start('Discovering source files...');
132
173
  const sourceFiles = await discoverFiles(projectRoot, opts.verbose);
174
+ spinner.stop();
133
175
 
134
176
  if (sourceFiles.length === 0) {
135
177
  console.log('No source files found to process.');
@@ -141,7 +183,11 @@ async function main() {
141
183
  }
142
184
 
143
185
  // Parse all source files
144
- const parsedFiles = await parseFiles(sourceFiles, projectRoot, opts.verbose);
186
+ spinner.start(`Parsing ${sourceFiles.length} files...`);
187
+ const parsedFiles = await parseFiles(sourceFiles, projectRoot, opts.verbose, (current, total, filePath) => {
188
+ spinner.update(`Parsing [${current}/${total}] ${filePath}`);
189
+ });
190
+ spinner.stop();
145
191
 
146
192
  if (parsedFiles.length === 0) {
147
193
  console.log('No files could be parsed.');
@@ -151,8 +197,18 @@ async function main() {
151
197
  // Run the recipe with streaming output
152
198
  const ctx = new ExecutionContext();
153
199
  let changeCount = 0;
200
+ let processedCount = 0;
201
+ const totalFiles = parsedFiles.length;
202
+
203
+ spinner.start(`Running recipe on ${totalFiles} files...`);
154
204
 
155
205
  for await (const result of scheduleRunStreaming(recipe, parsedFiles, ctx)) {
206
+ processedCount++;
207
+ const currentPath = result.after?.sourcePath ?? result.before?.sourcePath ?? '';
208
+ spinner.update(`Processing [${processedCount}/${totalFiles}] ${currentPath}`);
209
+
210
+ const statusMsg = `Processing [${processedCount}/${totalFiles}] ${currentPath}`;
211
+
156
212
  if (opts.dryRun) {
157
213
  // Print colorized diff immediately (skip empty diffs)
158
214
  const diff = await result.diff();
@@ -162,7 +218,9 @@ async function main() {
162
218
  );
163
219
  if (hasChanges) {
164
220
  changeCount++;
221
+ spinner.stop();
165
222
  console.log(colorizeDiff(diff));
223
+ spinner.start(statusMsg);
166
224
  }
167
225
  } else {
168
226
  // Apply changes immediately
@@ -175,20 +233,26 @@ async function main() {
175
233
  await fsp.writeFile(filePath, content);
176
234
 
177
235
  changeCount++;
236
+ spinner.stop();
178
237
  if (result.before) {
179
238
  console.log(` Modified: ${result.after.sourcePath}`);
180
239
  } else {
181
240
  console.log(` Created: ${result.after.sourcePath}`);
182
241
  }
242
+ spinner.start(statusMsg);
183
243
  } else if (result.before) {
184
244
  const filePath = path.join(projectRoot, result.before.sourcePath);
185
245
  await fsp.unlink(filePath);
186
246
  changeCount++;
247
+ spinner.stop();
187
248
  console.log(` Deleted: ${result.before.sourcePath}`);
249
+ spinner.start(statusMsg);
188
250
  }
189
251
  }
190
252
  }
191
253
 
254
+ spinner.stop();
255
+
192
256
  if (changeCount === 0) {
193
257
  console.log('No changes to make.');
194
258
  } else if (opts.dryRun) {
@@ -17,6 +17,37 @@
17
17
  import {J} from "../java";
18
18
  import {produce} from "immer";
19
19
 
20
+ /**
21
+ * Gets the effective last whitespace from a Space.
22
+ * When there are comments, the last whitespace is the suffix of the last comment.
23
+ * When there are no comments, it's the whitespace property.
24
+ */
25
+ export function lastWhitespace(space: J.Space): string {
26
+ if (space.comments.length > 0) {
27
+ return space.comments[space.comments.length - 1].suffix;
28
+ }
29
+ return space.whitespace;
30
+ }
31
+
32
+ /**
33
+ * Replaces the effective last whitespace in a Space using a transform function.
34
+ * When there are comments, updates the suffix of the last comment.
35
+ * When there are no comments, updates the whitespace property.
36
+ *
37
+ * @param space The Space to modify (Immer draft)
38
+ * @param transform Function that receives the current last whitespace and returns the new value
39
+ */
40
+ export function replaceLastWhitespace(space: J.Space, transform: (ws: string) => string): J.Space {
41
+ return produce(space, draft => {
42
+ if (draft.comments.length > 0) {
43
+ const lastComment = draft.comments[draft.comments.length - 1];
44
+ lastComment.suffix = transform(lastComment.suffix);
45
+ } else {
46
+ draft.whitespace = transform(draft.whitespace);
47
+ }
48
+ });
49
+ }
50
+
20
51
  /**
21
52
  * Handles element removal from lists while preserving LST formatting.
22
53
  * Automatically applies prefixes from removed elements to the next kept element,
package/src/java/index.ts CHANGED
@@ -18,5 +18,6 @@ export * from "./tree";
18
18
  export * from "./markers";
19
19
  export * from "./visitor";
20
20
  export * from "./type-visitor";
21
+ export * from "./formatting-utils";
21
22
 
22
23
  import "./print";