@@ -110,26 +110,23 @@ export class RemoveImport<P> extends JavaScriptVisitor<P> {
110110
111111 // Now process imports with knowledge of what's used
112112 return this . produceJavaScript < JS . CompilationUnit > ( compilationUnit , p , async draft => {
113- let nextStatementPrefix : J . Space | undefined ;
113+ let previousWasRemoved = false ;
114+ let hasKeptStatement = false ;
114115
115- draft . statements = await mapAsync ( compilationUnit . statements , async ( stmt , index ) => {
116+ draft . statements = await mapAsync ( compilationUnit . statements , async ( stmt ) => {
116117 const statement = stmt . element ;
117118
118119 // Handle ES6 imports
119120 if ( statement ?. kind === JS . Kind . Import ) {
120121 const jsImport = statement as JS . Import ;
121122 const result = await this . processImport ( jsImport , usedIdentifiers , usedTypes , p ) ;
122123 if ( result === undefined ) {
123- // Store the prefix for the next statement to avoid leaving blank lines
124- if ( index < compilationUnit . statements . length - 1 ) {
125- const nextStmt = compilationUnit . statements [ index + 1 ] ;
126- if ( nextStmt ?. element ) {
127- nextStatementPrefix = jsImport . prefix ;
128- }
129- }
130- // Remove the entire import
124+ // Mark that we removed an import
125+ previousWasRemoved = true ;
131126 return undefined ;
132127 }
128+ previousWasRemoved = false ;
129+ hasKeptStatement = true ;
133130 return { ...stmt , element : result } ;
134131 }
135132
@@ -139,29 +136,25 @@ export class RemoveImport<P> extends JavaScriptVisitor<P> {
139136 const varDecl = statement as J . VariableDeclarations ;
140137 const result = await this . processRequireFromVarDecls ( varDecl , usedIdentifiers , p ) ;
141138 if ( result === undefined ) {
142- // Store the prefix for the next statement to avoid leaving blank lines
143- if ( index < compilationUnit . statements . length - 1 ) {
144- const nextStmt = compilationUnit . statements [ index + 1 ] ;
145- if ( nextStmt ?. element ) {
146- nextStatementPrefix = varDecl . prefix ;
147- }
148- }
149- // Remove the entire require statement
139+ // Mark that we removed a require
140+ previousWasRemoved = true ;
150141 return undefined ;
151142 }
143+ previousWasRemoved = false ;
144+ hasKeptStatement = true ;
152145 return { ...stmt , element : result } ;
153146 }
154147
155- // Apply stored prefix if this statement follows a removed import
156- if ( nextStatementPrefix && statement ) {
157- const updatedStatement = await this . visit ( statement , p ) as J ;
158- if ( updatedStatement ) {
159- ( updatedStatement as any ) . prefix = nextStatementPrefix ;
160- nextStatementPrefix = undefined ;
161- return { ...stmt , element : updatedStatement } ;
162- }
148+ // If the previous statement was removed, adjust this statement's prefix
149+ if ( previousWasRemoved && statement ) {
150+ previousWasRemoved = false ;
151+ const updatedStatement = this . adjustPrefixAfterRemoval ( statement , hasKeptStatement ) ;
152+ hasKeptStatement = true ;
153+ return { ...stmt , element : updatedStatement } ;
163154 }
164155
156+ previousWasRemoved = false ;
157+ hasKeptStatement = true ;
165158 return stmt ;
166159 } ) ;
167160
@@ -171,6 +164,53 @@ export class RemoveImport<P> extends JavaScriptVisitor<P> {
171164 } ) ;
172165 }
173166
167+ /**
168+ * Adjusts the prefix of a statement that follows a removed import/require.
169+ * Removes the import's line while preserving comments that belong to subsequent lines.
170+ *
171+ * If the prefix whitespace doesn't contain a newline, the first comment was inline
172+ * on the removed import line - we remove that comment and one line terminator from its suffix.
173+ *
174+ * @param statement The statement following the removed import
175+ * @param hasKeptPreviousStatement Whether there's a previous statement that wasn't removed
176+ */
177+ private adjustPrefixAfterRemoval ( statement : J , hasKeptPreviousStatement : boolean ) : J {
178+ const prefix = ( statement as any ) . prefix ;
179+ if ( ! prefix ) {
180+ return statement ;
181+ }
182+
183+ let whitespace = prefix . whitespace || '' ;
184+ let comments = prefix . comments || [ ] ;
185+
186+ // If the whitespace before the first comment doesn't contain a line terminator,
187+ // the first comment was inline on the same line as the removed import
188+ if ( ! / [ \r \n ] / . test ( whitespace ) && comments . length > 0 ) {
189+ // Remove the first comment (which was inline with the import)
190+ comments = comments . slice ( 1 ) ;
191+ // Clear the whitespace (it was just spacing before the inline comment)
192+ whitespace = '' ;
193+ } else if ( ! hasKeptPreviousStatement ) {
194+ // At the beginning of file - remove all leading newlines
195+ // (The removed import was the first statement, so we don't want leading blank lines)
196+ whitespace = whitespace . replace ( / ^ [ \r \n ] + / , '' ) ;
197+ }
198+ // else: There's a previous kept statement and no inline comment - keep whitespace as-is
199+ // (This preserves intentional blank lines between statements)
200+
201+ // Create a new Space with adjusted whitespace and comments
202+ const newPrefix = {
203+ ...prefix ,
204+ whitespace : whitespace ,
205+ comments : comments
206+ } ;
207+
208+ return {
209+ ...statement ,
210+ prefix : newPrefix
211+ } as J ;
212+ }
213+
174214 private async processImport (
175215 jsImport : JS . Import ,
176216 usedIdentifiers : Set < string > ,
0 commit comments