@@ -42,8 +42,14 @@ export default function transform(root: SgRoot<Js>): string | null {
42
42
}
43
43
} ) ;
44
44
45
- const hasOnlyBuiltinModules = properties . length === 1 &&
46
- properties [ 0 ] . text ( ) === "builtinModules" ;
45
+ const pairProperties = objectPattern . findAll ( {
46
+ rule : {
47
+ kind : "pair_pattern"
48
+ }
49
+ } ) ;
50
+
51
+ const hasOnlyBuiltinModules = ( properties . length === 1 && properties [ 0 ] . text ( ) === "builtinModules" ) ||
52
+ ( properties . length === 0 && pairProperties . length === 1 && pairProperties [ 0 ] . text ( ) . includes ( "builtinModules" ) ) ;
47
53
48
54
if ( hasOnlyBuiltinModules ) {
49
55
// Case 2: Replace entire require statement
@@ -54,37 +60,50 @@ export default function transform(root: SgRoot<Js>): string | null {
54
60
} ) ;
55
61
if ( moduleSpecifier ) {
56
62
const currentModule = moduleSpecifier . text ( ) ;
57
- const newModule = currentModule . includes ( "node:" ) ? '" node:module"' : '" module"' ;
63
+ const newModule = currentModule . includes ( "node:" ) ? "' node:module'" : "' module'" ;
58
64
edits . push ( moduleSpecifier . replace ( newModule ) ) ;
59
65
hasChanges = true ;
60
66
}
61
67
} else {
62
68
// Case 3: Split into two statements
63
- const newText = originalText . replace ( / , ? \s * b u i l t i n M o d u l e s \s * , ? / g, "" ) . replace ( / , \s * $ / , "" ) . replace ( / ^ \s * , / , "" ) ;
69
+ const newText = originalText . replace ( / , ? \s * b u i l t i n M o d u l e s \s * ( : \s * \w + ) ? \s * , ? / g, "" ) . replace ( / , \s * $ / , "" ) . replace ( / ^ \s * , / , "" ) ;
64
70
65
71
if ( newText !== "{ }" ) {
66
- edits . push ( objectPattern . replace ( newText ) ) ;
67
- }
72
+ // Get the parent variable declaration to replace the entire statement
73
+ const variableDeclaration = statement . parent ( ) ;
68
74
69
- // Add new module require statement
70
- const moduleSpecifier = statement . find ( {
71
- rule : {
72
- kind : "string"
73
- }
74
- } ) ;
75
- if ( moduleSpecifier ) {
76
- const currentModule = moduleSpecifier . text ( ) ;
77
- const newModule = currentModule . includes ( "node:" ) ? "node:module" : "module" ;
78
- const newStatement = `const { builtinModules } = require(${ newModule . includes ( "node:" ) ? '"node:module"' : '"module"' } );` ;
75
+ if ( variableDeclaration && ( variableDeclaration . kind ( ) === "variable_declaration" || variableDeclaration . kind ( ) === "lexical_declaration" ) ) {
76
+ // Extract the alias if present
77
+ const aliasMatch = originalText . match ( / b u i l t i n M o d u l e s \s * : \s * ( \w + ) / ) ;
78
+ const aliasText = aliasMatch ? `: ${ aliasMatch [ 1 ] } ` : "" ;
79
79
80
- // Insert after current statement
81
- const statementEnd = statement . range ( ) . end ;
82
- edits . push ( {
83
- startPos : statementEnd . index ,
84
- endPos : statementEnd . index ,
85
- insertedText : `\n${ newStatement } `
86
- } ) ;
87
- hasChanges = true ;
80
+ const moduleSpecifier = statement . find ( {
81
+ rule : {
82
+ kind : "string"
83
+ }
84
+ } ) ;
85
+
86
+ if ( moduleSpecifier ) {
87
+ const currentModule = moduleSpecifier . text ( ) ;
88
+ const newModule = currentModule . includes ( "node:" ) ? "node:module" : "module" ;
89
+
90
+ // Create the new statements
91
+ const firstStatement = `const ${ newText } = require(${ currentModule } );` ;
92
+ const secondStatement = `const { builtinModules${ aliasText } } = require(${ newModule . includes ( 'node:' ) ? "'node:module'" : "'module'" } );` ;
93
+
94
+ // Replace the entire variable declaration with both statements
95
+ const replacementText = `${ firstStatement } \n${ secondStatement } ` ;
96
+
97
+ edits . push ( variableDeclaration . replace ( replacementText ) ) ;
98
+ hasChanges = true ;
99
+ }
100
+ } else {
101
+ // Fallback to just replacing the object pattern
102
+ edits . push ( objectPattern . replace ( newText ) ) ;
103
+ }
104
+ } else {
105
+ // If we're removing the entire destructuring, we need to remove the whole statement
106
+ edits . push ( statement . replace ( "" ) ) ;
88
107
}
89
108
}
90
109
}
@@ -115,6 +134,14 @@ export default function transform(root: SgRoot<Js>): string | null {
115
134
} ) ;
116
135
117
136
if ( memberExpressions . length > 0 ) {
137
+ // Replace variable name from repl to module
138
+ edits . push ( identifier . replace ( "module" ) ) ;
139
+
140
+ // Replace all member expressions
141
+ for ( const memberExpr of memberExpressions ) {
142
+ edits . push ( memberExpr . replace ( "module.builtinModules" ) ) ;
143
+ }
144
+
118
145
// Replace require statement to use module instead
119
146
const moduleSpecifier = statement . find ( {
120
147
rule : {
@@ -123,7 +150,7 @@ export default function transform(root: SgRoot<Js>): string | null {
123
150
} ) ;
124
151
if ( moduleSpecifier ) {
125
152
const currentModule = moduleSpecifier . text ( ) ;
126
- const newModule = currentModule . includes ( "node:" ) ? '" node:module"' : '" module"' ;
153
+ const newModule = currentModule . includes ( "node:" ) ? "' node:module'" : "' module'" ;
127
154
edits . push ( moduleSpecifier . replace ( newModule ) ) ;
128
155
hasChanges = true ;
129
156
}
@@ -138,6 +165,7 @@ export default function transform(root: SgRoot<Js>): string | null {
138
165
const replImportStatements = getNodeImportStatements ( root , "repl" ) ;
139
166
140
167
for ( const statement of replImportStatements ) {
168
+ // Handle named imports like: import { builtinModules } from 'node:repl'
141
169
const namedImports = statement . find ( {
142
170
rule : {
143
171
kind : "named_imports"
@@ -167,13 +195,13 @@ export default function transform(root: SgRoot<Js>): string | null {
167
195
} ) ;
168
196
if ( moduleSpecifier ) {
169
197
const currentModule = moduleSpecifier . text ( ) ;
170
- const newModule = currentModule . includes ( "node:" ) ? '" node:module"' : '" module"' ;
198
+ const newModule = currentModule . includes ( "node:" ) ? "' node:module'" : "' module'" ;
171
199
edits . push ( moduleSpecifier . replace ( newModule ) ) ;
172
200
hasChanges = true ;
173
201
}
174
202
} else {
175
203
// Case 5: Split into two statements
176
- const newText = originalText . replace ( / , ? \s * b u i l t i n M o d u l e s \s * , ? / g, "" ) . replace ( / , \s * $ / , "" ) . replace ( / ^ \s * , / , "" ) ;
204
+ const newText = originalText . replace ( / , ? \s * b u i l t i n M o d u l e s \s * ( : \s * \w + ) ? \s * , ? / g, "" ) . replace ( / , \s * $ / , "" ) . replace ( / ^ \s * , / , "" ) ;
177
205
edits . push ( namedImports . replace ( newText ) ) ;
178
206
179
207
// Add new module import statement
@@ -185,7 +213,12 @@ export default function transform(root: SgRoot<Js>): string | null {
185
213
if ( moduleSpecifier ) {
186
214
const currentModule = moduleSpecifier . text ( ) ;
187
215
const newModule = currentModule . includes ( "node:" ) ? "node:module" : "module" ;
188
- const newStatement = `import { builtinModules } from ${ newModule . includes ( "node:" ) ? '"node:module"' : '"module"' } ;` ;
216
+
217
+ // Extract the alias if present
218
+ const aliasMatch = originalText . match ( / b u i l t i n M o d u l e s \s * ( a s \s + \w + ) / ) ;
219
+ const aliasText = aliasMatch ? ` ${ aliasMatch [ 1 ] } ` : "" ;
220
+
221
+ const newStatement = `import { builtinModules${ aliasText } } from ${ newModule . includes ( "node:" ) ? "'node:module'" : "'module'" } ;` ;
189
222
190
223
// Insert after current statement
191
224
const statementEnd = statement . range ( ) . end ;
@@ -199,6 +232,74 @@ export default function transform(root: SgRoot<Js>): string | null {
199
232
}
200
233
}
201
234
}
235
+
236
+ // Handle default imports like: import repl from 'node:repl'
237
+ // Handle namespace imports like: import * as repl from 'node:repl'
238
+ if ( ! namedImports ) {
239
+ // Look for default or namespace imports that use repl.builtinModules
240
+ const importClause = statement . find ( {
241
+ rule : {
242
+ kind : "import_clause"
243
+ }
244
+ } ) ;
245
+
246
+ if ( importClause ) {
247
+ let importIdentifier = null ;
248
+
249
+ // Check for default import
250
+ const defaultImport = importClause . find ( {
251
+ rule : {
252
+ kind : "identifier"
253
+ }
254
+ } ) ;
255
+
256
+ // Check for namespace import
257
+ const namespaceImport = importClause . find ( {
258
+ rule : {
259
+ kind : "namespace_import"
260
+ }
261
+ } ) ;
262
+
263
+ if ( defaultImport ) {
264
+ importIdentifier = defaultImport ;
265
+ } else if ( namespaceImport ) {
266
+ const namespaceIdentifier = namespaceImport . find ( {
267
+ rule : {
268
+ kind : "identifier"
269
+ }
270
+ } ) ;
271
+ if ( namespaceIdentifier ) {
272
+ importIdentifier = namespaceIdentifier ;
273
+ }
274
+ }
275
+
276
+ if ( importIdentifier ) {
277
+ const varName = importIdentifier . text ( ) ;
278
+
279
+ // Find all member expressions using this variable with builtinModules
280
+ const memberExpressions = rootNode . findAll ( {
281
+ rule : {
282
+ pattern : `${ varName } .builtinModules`
283
+ }
284
+ } ) ;
285
+
286
+ if ( memberExpressions . length > 0 ) {
287
+ // Replace the import to use module instead
288
+ const moduleSpecifier = statement . find ( {
289
+ rule : {
290
+ kind : "string"
291
+ }
292
+ } ) ;
293
+ if ( moduleSpecifier ) {
294
+ const currentModule = moduleSpecifier . text ( ) ;
295
+ const newModule = currentModule . includes ( "node:" ) ? "'node:module'" : "'module'" ;
296
+ edits . push ( moduleSpecifier . replace ( newModule ) ) ;
297
+ hasChanges = true ;
298
+ }
299
+ }
300
+ }
301
+ }
302
+ }
202
303
}
203
304
204
305
if ( ! hasChanges ) return null ;
0 commit comments