11import { EOL } from 'node:os' ;
22import dedent from 'dedent' ;
3- import { getNodeRequireCalls } from '@nodejs/codemod-utils/ast-grep/require-call' ;
4- import {
5- getNodeImportCalls ,
6- getNodeImportStatements ,
7- } from '@nodejs/codemod-utils/ast-grep/import-statement' ;
83import { resolveBindingPath } from '@nodejs/codemod-utils/ast-grep/resolve-binding-path' ;
94import { removeBinding } from '@nodejs/codemod-utils/ast-grep/remove-binding' ;
105import { removeLines } from '@nodejs/codemod-utils/ast-grep/remove-lines' ;
@@ -16,6 +11,7 @@ import type {
1611 SgRoot ,
1712} from '@codemod.com/jssg-types/main' ;
1813import type Js from '@codemod.com/jssg-types/langs/javascript' ;
14+ import { getModuleDependencies } from '@nodejs/codemod-utils/ast-grep/module-dependencies' ;
1915
2016type BindingToReplace = {
2117 rule : Rule < Js > ;
@@ -36,6 +32,16 @@ type CreateOptionsType = {
3632 payloadKind ?: 'json' | 'form' ;
3733} ;
3834
35+ type AxiosMethodUpdateConfig = {
36+ name : string ;
37+ method ?: string ;
38+ oldOptionsIndex : number ;
39+ bodyIndex ?: number ;
40+ payloadKind ?: NonNullable < CreateOptionsType [ 'payloadKind' ] > ;
41+ responseAlias ?: string ;
42+ optionalOptionsArg ?: boolean ;
43+ } ;
44+
3945const formatLocation = ( {
4046 root,
4147 node,
@@ -124,210 +130,98 @@ const getFormBodyExpression = (
124130 ` ;
125131} ;
126132
127- const baseUpdates : {
128- oldBind : string ;
129- replaceFn : BindingToReplace [ 'replaceFn' ] ;
130- supportDefaultAccess ?: boolean ;
131- } [ ] = [
132- {
133- oldBind : '$.get' ,
134- replaceFn : ( args , context ) => {
135- const [ url , oldOptions ] = args ;
136- if ( ! url ) {
137- warnWithLocation ( context , 'Missing URL in axios.get. Skipping.' ) ;
138- return '' ;
139- }
140- const options = createOptions ( { oldOptions } ) ;
141- return dedent . withOptions ( { alignValues : true } ) `
142- fetch(${ url . text ( ) } ${ options ? `, ${ options } ` : '' } )
143- .then(async (res) => Object.assign(res, { data: await res.json() }))
144- .catch(() => null)
145- ` ;
146- } ,
147- } ,
148- {
149- oldBind : '$.post' ,
150- replaceFn : ( args , context ) => {
151- const url = args [ 0 ] ;
152- if ( ! url ) {
153- warnWithLocation ( context , 'Missing URL in axios.post. Skipping.' ) ;
154- return '' ;
155- }
156- const options = createOptions ( {
157- oldOptions : args [ 2 ] ,
158- method : 'POST' ,
159- bodyNode : args [ 1 ] ?? null ,
160- payloadKind : 'json' ,
161- } ) ;
162- return dedent . withOptions ( { alignValues : true } ) `
163- fetch(${ url . text ( ) } ${ options ? `, ${ options } ` : '' } )
164- .then(async (resp) => Object.assign(resp, { data: await resp.json() }))
165- .catch(() => null)
166- ` ;
167- } ,
168- } ,
169- {
170- oldBind : '$.put' ,
171- replaceFn : ( args , context ) => {
172- const url = args [ 0 ] ;
173- if ( ! url ) {
174- warnWithLocation ( context , 'Missing URL in axios.put. Skipping.' ) ;
175- return '' ;
176- }
177- const options = createOptions ( {
178- oldOptions : args [ 2 ] ,
179- method : 'PUT' ,
180- bodyNode : args [ 1 ] ?? null ,
181- payloadKind : 'json' ,
182- } ) ;
183- return dedent . withOptions ( { alignValues : true } ) `
184- fetch(${ url . text ( ) } ${ options ? `, ${ options } ` : '' } )
185- .then(async (resp) => Object.assign(resp, { data: await resp.json() }))
186- .catch(() => null)
187- ` ;
188- } ,
189- } ,
190- {
191- oldBind : '$.patch' ,
192- replaceFn : ( args , context ) => {
193- const url = args [ 0 ] ;
194- if ( ! url ) {
195- warnWithLocation ( context , 'Missing URL in axios.patch. Skipping.' ) ;
196- return '' ;
197- }
198- const options = createOptions ( {
199- oldOptions : args [ 2 ] ,
200- method : 'PATCH' ,
201- bodyNode : args [ 1 ] ?? null ,
202- payloadKind : 'json' ,
203- } ) ;
204- return dedent . withOptions ( { alignValues : true } ) `
205- fetch(${ url . text ( ) } ${ options ? `, ${ options } ` : '' } )
206- .then(async (resp) => Object.assign(resp, { data: await resp.json() }))
133+ const createAxiosMethodUpdate = ( {
134+ name,
135+ method,
136+ oldOptionsIndex,
137+ bodyIndex,
138+ payloadKind = 'json' ,
139+ responseAlias = 'resp' ,
140+ optionalOptionsArg = true ,
141+ } : AxiosMethodUpdateConfig ) => ( {
142+ oldBind : `$.${ name } ` ,
143+ replaceFn : ( args : SgNode < Js > [ ] , context : WarningContext ) => {
144+ const url = args [ 0 ] ;
145+ if ( ! url ) {
146+ warnWithLocation ( context , `Missing URL in axios.${ name } . Skipping.` ) ;
147+ return '' ;
148+ }
149+
150+ const options = createOptions ( {
151+ oldOptions : args [ oldOptionsIndex ] ,
152+ method,
153+ bodyNode : bodyIndex === undefined ? undefined : ( args [ bodyIndex ] ?? null ) ,
154+ payloadKind,
155+ } ) ;
156+
157+ const fetchCall = optionalOptionsArg
158+ ? `fetch(${ url . text ( ) } ${ options ? `, ${ options } ` : '' } )`
159+ : `fetch(${ url . text ( ) } , ${ options } )` ;
160+
161+ return dedent . withOptions ( { alignValues : true } ) `
162+ ${ fetchCall }
163+ .then(async (${ responseAlias } ) => Object.assign(${ responseAlias } , { data: await ${ responseAlias } .json() }))
207164 .catch(() => null)
208165 ` ;
209- } ,
210166 } ,
167+ } ) ;
168+
169+ const axiosMethodUpdates : AxiosMethodUpdateConfig [ ] = [
170+ { name : 'post' , method : 'POST' , oldOptionsIndex : 2 , bodyIndex : 1 } ,
171+ { name : 'put' , method : 'PUT' , oldOptionsIndex : 2 , bodyIndex : 1 } ,
172+ { name : 'patch' , method : 'PATCH' , oldOptionsIndex : 2 , bodyIndex : 1 } ,
211173 {
212- oldBind : '$.postForm' ,
213- replaceFn : ( args , context ) => {
214- const url = args [ 0 ] ;
215- if ( ! url ) {
216- warnWithLocation ( context , 'Missing URL in axios.postForm. Skipping.' ) ;
217- return '' ;
218- }
219- const options = createOptions ( {
220- oldOptions : args [ 2 ] ,
221- method : 'POST' ,
222- bodyNode : args [ 1 ] ?? null ,
223- payloadKind : 'form' ,
224- } ) ;
225- return dedent . withOptions ( { alignValues : true } ) `
226- fetch(${ url . text ( ) } ${ options ? `, ${ options } ` : '' } )
227- .then(async (resp) => Object.assign(resp, { data: await resp.json() }))
228- .catch(() => null)
229- ` ;
230- } ,
174+ name : 'postForm' ,
175+ method : 'POST' ,
176+ oldOptionsIndex : 2 ,
177+ bodyIndex : 1 ,
178+ payloadKind : 'form' ,
231179 } ,
232180 {
233- oldBind : '$.putForm' ,
234- replaceFn : ( args , context ) => {
235- const url = args [ 0 ] ;
236- if ( ! url ) {
237- warnWithLocation ( context , 'Missing URL in axios.putForm. Skipping.' ) ;
238- return '' ;
239- }
240- const options = createOptions ( {
241- oldOptions : args [ 2 ] ,
242- method : 'PUT' ,
243- bodyNode : args [ 1 ] ?? null ,
244- payloadKind : 'form' ,
245- } ) ;
246- return dedent . withOptions ( { alignValues : true } ) `
247- fetch(${ url . text ( ) } ${ options ? `, ${ options } ` : '' } )
248- .then(async (resp) => Object.assign(resp, { data: await resp.json() }))
249- .catch(() => null)
250- ` ;
251- } ,
181+ name : 'putForm' ,
182+ method : 'PUT' ,
183+ oldOptionsIndex : 2 ,
184+ bodyIndex : 1 ,
185+ payloadKind : 'form' ,
252186 } ,
253187 {
254- oldBind : '$.patchForm' ,
255- replaceFn : ( args , context ) => {
256- const url = args [ 0 ] ;
257- if ( ! url ) {
258- warnWithLocation ( context , 'Missing URL in axios.patchForm. Skipping.' ) ;
259- return '' ;
260- }
261- const options = createOptions ( {
262- oldOptions : args [ 2 ] ,
263- method : 'PATCH' ,
264- bodyNode : args [ 1 ] ?? null ,
265- payloadKind : 'form' ,
266- } ) ;
267- return dedent . withOptions ( { alignValues : true } ) `
268- fetch(${ url . text ( ) } ${ options ? `, ${ options } ` : '' } )
269- .then(async (resp) => Object.assign(resp, { data: await resp.json() }))
270- .catch(() => null)
271- ` ;
272- } ,
188+ name : 'patchForm' ,
189+ method : 'PATCH' ,
190+ oldOptionsIndex : 2 ,
191+ bodyIndex : 1 ,
192+ payloadKind : 'form' ,
273193 } ,
274194 {
275- oldBind : '$.delete' ,
276- replaceFn : ( args , context ) => {
277- const url = args [ 0 ] ;
278- if ( ! url ) {
279- warnWithLocation ( context , 'Missing URL in axios.delete. Skipping.' ) ;
280- return '' ;
281- }
282- const options = createOptions ( {
283- oldOptions : args [ 1 ] ,
284- method : 'DELETE' ,
285- } ) ;
286- return dedent . withOptions ( { alignValues : true } ) `
287- fetch(${ url . text ( ) } , ${ options } )
288- .then(async (resp) => Object.assign(resp, { data: await resp.json() }))
289- .catch(() => null)
290- ` ;
291- } ,
195+ name : 'delete' ,
196+ method : 'DELETE' ,
197+ oldOptionsIndex : 1 ,
198+ optionalOptionsArg : false ,
292199 } ,
293200 {
294- oldBind : '$.head' ,
295- replaceFn : ( args , context ) => {
296- const url = args [ 0 ] ;
297- if ( ! url ) {
298- warnWithLocation ( context , 'Missing URL in axios.head. Skipping.' ) ;
299- return '' ;
300- }
301- const options = createOptions ( {
302- oldOptions : args [ 1 ] ,
303- method : 'HEAD' ,
304- } ) ;
305- return dedent . withOptions ( { alignValues : true } ) `
306- fetch(${ url . text ( ) } , ${ options } )
307- .then(async (resp) => Object.assign(resp, { data: await resp.json() }))
308- .catch(() => null)
309- ` ;
310- } ,
201+ name : 'head' ,
202+ method : 'HEAD' ,
203+ oldOptionsIndex : 1 ,
204+ optionalOptionsArg : false ,
311205 } ,
312206 {
313- oldBind : '$.options' ,
314- replaceFn : ( args , context ) => {
315- const url = args [ 0 ] ;
316- if ( ! url ) {
317- warnWithLocation ( context , 'Missing URL in axios.options. Skipping.' ) ;
318- return '' ;
319- }
320- const options = createOptions ( {
321- oldOptions : args [ 1 ] ,
322- method : 'OPTIONS' ,
323- } ) ;
324- return dedent . withOptions ( { alignValues : true } ) `
325- fetch(${ url . text ( ) } , ${ options } )
326- .then(async (resp) => Object.assign(resp, { data: await resp.json() }))
327- .catch(() => null)
328- ` ;
329- } ,
207+ name : 'options' ,
208+ method : 'OPTIONS' ,
209+ oldOptionsIndex : 1 ,
210+ optionalOptionsArg : false ,
330211 } ,
212+ ] ;
213+
214+ const baseUpdates : {
215+ oldBind : string ;
216+ replaceFn : BindingToReplace [ 'replaceFn' ] ;
217+ supportDefaultAccess ?: boolean ;
218+ } [ ] = [
219+ createAxiosMethodUpdate ( {
220+ name : 'get' ,
221+ oldOptionsIndex : 1 ,
222+ responseAlias : 'res' ,
223+ } ) ,
224+ ...axiosMethodUpdates . map ( createAxiosMethodUpdate ) ,
331225 {
332226 oldBind : '$.request' ,
333227 replaceFn : ( args , context ) => {
@@ -475,11 +369,7 @@ export default function transform(root: SgRoot<Js>): string | null {
475369 const linesToRemove : Range [ ] = [ ] ;
476370 const bindsToReplace : BindingToReplace [ ] = [ ] ;
477371
478- const importRequireStatement = [
479- ...getNodeRequireCalls ( root , 'axios' ) ,
480- ...getNodeImportStatements ( root , 'axios' ) ,
481- ...getNodeImportCalls ( root , 'axios' ) ,
482- ] ;
372+ const importRequireStatement = getModuleDependencies ( root , 'axios' ) ;
483373
484374 if ( ! importRequireStatement . length ) return null ;
485375
0 commit comments