Skip to content

Commit a6d4eba

Browse files
add unsuported api
1 parent 1284d39 commit a6d4eba

15 files changed

Lines changed: 239 additions & 0 deletions

recipes/axios-to-whatwg-fetch/src/workflow.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,25 @@ const warnWithLocation = (
6565
console.warn(`[Codemod] ${message} (at ${location})`);
6666
};
6767

68+
const UNSUPPORTED_CONFIG_OPTIONS = [
69+
'transformRequest',
70+
'transformResponse',
71+
'paramsSerializer',
72+
'withCredentials',
73+
'timeout',
74+
'httpAgent',
75+
'httpsAgent',
76+
'validateStatus',
77+
'maxRedirects',
78+
'socketPath',
79+
'decompress',
80+
'maxContentLength',
81+
'maxBodyLength',
82+
'beforeRedirect',
83+
'cancelToken',
84+
'signal',
85+
];
86+
6887
const getObjectPropertyValue = (
6988
objectNode: SgNode<Js>,
7089
propertyName: string,
@@ -84,6 +103,23 @@ const getObjectPropertyValue = (
84103
return pair?.field('value');
85104
};
86105

106+
const hasUnsupportedOptions = (
107+
configNode: SgNode<Js> | undefined,
108+
): { unsupported: boolean; optionName?: string } => {
109+
if (!configNode || configNode.kind() !== 'object') {
110+
return { unsupported: false };
111+
}
112+
113+
for (const optionName of UNSUPPORTED_CONFIG_OPTIONS) {
114+
const option = getObjectPropertyValue(configNode, optionName);
115+
if (option) {
116+
return { unsupported: true, optionName };
117+
}
118+
}
119+
120+
return { unsupported: false };
121+
};
122+
87123
const getBodyExpression = (
88124
bodyNode: SgNode<Js>,
89125
payloadKind: NonNullable<CreateOptionsType['payloadKind']>,
@@ -357,6 +393,68 @@ const createOptions = ({
357393
return `{${EOL}${optionParts.join(`,${EOL}`)}${EOL}}`;
358394
};
359395

396+
/**
397+
* Checks if any axios calls in the file use unsupported configuration options.
398+
* If found, logs a warning and returns true to indicate the file should be skipped.
399+
*/
400+
const checkForUnsupportedOptions = (
401+
rootNode: SgNode<Js>,
402+
bindsToReplace: BindingToReplace[],
403+
root: SgRoot<Js>,
404+
): boolean => {
405+
for (const bind of bindsToReplace) {
406+
const matches = rootNode.findAll({
407+
rule: bind.rule,
408+
});
409+
410+
for (const match of matches) {
411+
const argsAndCommaas = match.getMultipleMatches('ARG');
412+
const args = argsAndCommaas.filter((arg) => arg.text() !== ',');
413+
414+
// Check axios.request() - first arg should be config object
415+
if (bind.binding.endsWith('.request')) {
416+
const config = args[0];
417+
const unsupported = hasUnsupportedOptions(config);
418+
if (unsupported.unsupported) {
419+
warnWithLocation(
420+
{ root, match },
421+
`Unsupported axios configuration option '${unsupported.optionName}' detected in axios.request. Skipping migration to preserve functionality.`,
422+
config,
423+
);
424+
return true;
425+
}
426+
} else {
427+
// For other methods (get, post, put, patch, delete, head, options)
428+
// Determine option index based on method
429+
let optionIndex = 1;
430+
if (
431+
bind.binding.endsWith('.post') ||
432+
bind.binding.endsWith('.put') ||
433+
bind.binding.endsWith('.patch') ||
434+
bind.binding.endsWith('.postForm') ||
435+
bind.binding.endsWith('.putForm') ||
436+
bind.binding.endsWith('.patchForm')
437+
) {
438+
optionIndex = 2;
439+
}
440+
441+
const config = args[optionIndex];
442+
const unsupported = hasUnsupportedOptions(config);
443+
if (unsupported.unsupported) {
444+
const methodName = bind.binding.split('.').pop();
445+
warnWithLocation(
446+
{ root, match },
447+
`Unsupported axios configuration option '${unsupported.optionName}' detected in axios.${methodName}. Skipping migration to preserve functionality.`,
448+
config,
449+
);
450+
return true;
451+
}
452+
}
453+
}
454+
}
455+
return false;
456+
};
457+
360458
/**
361459
* Transforms the AST root by replacing axios bindings with Fetch API calls.
362460
*
@@ -390,6 +488,11 @@ export default function transform(root: SgRoot<Js>): string | null {
390488
}
391489
}
392490

491+
// Check for unsupported options before making any changes
492+
if (checkForUnsupportedOptions(rootNode, bindsToReplace, root)) {
493+
return null;
494+
}
495+
393496
for (const bind of bindsToReplace) {
394497
const matches = rootNode.findAll({
395498
rule: bind.rule,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import axios from 'axios';
2+
3+
// This should be migratable
4+
const todos = await axios.get('https://api.example.com/todos');
5+
6+
// But this one has unsupported transformRequest, so entire file should be skipped
7+
const data = await axios.post('https://api.example.com/data', { foo: 'bar' }, {
8+
transformRequest: [(data) => JSON.stringify(data)],
9+
});
10+
11+
console.log(todos, data);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import axios from 'axios';
2+
3+
const response = await axios.get('https://api.example.com/items', {
4+
headers: { 'Authorization': 'Bearer token' },
5+
});
6+
7+
const created = await axios.request({
8+
url: 'https://api.example.com/create',
9+
method: 'POST',
10+
data: { item: 'test' },
11+
validateStatus: (status) => status < 500,
12+
});
13+
14+
console.log(response, created);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import axios from 'axios';
2+
3+
// These are all migratable
4+
const users = await axios.get('https://api.example.com/users');
5+
const posts = await axios.get('https://api.example.com/posts');
6+
7+
// But this has unsupported timeout, so entire file is skipped
8+
const special = await axios.post('https://api.example.com/special', { data: 'test' }, {
9+
timeout: 5000,
10+
});
11+
12+
console.log(users, posts, special);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import axios from 'axios';
2+
3+
const response = await axios.request({
4+
url: 'https://api.example.com/data',
5+
method: 'POST',
6+
data: { foo: 'bar' },
7+
transformRequest: [(data) => data],
8+
transformResponse: [(data) => data],
9+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import axios from 'axios';
2+
3+
const response = await axios.get('https://api.example.com/data', {
4+
params: { foo: 'bar' },
5+
paramsSerializer: (params) => {
6+
return Object.entries(params).map(([k, v]) => `${k}=${v}`).join('&');
7+
},
8+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import axios from 'axios';
2+
3+
const response = await axios.post('https://api.example.com/data', { foo: 'bar' }, {
4+
transformRequest: [(data) => {
5+
return JSON.stringify(data);
6+
}],
7+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import axios from 'axios';
2+
3+
const response = await axios.get('https://api.example.com/data', {
4+
transformResponse: [(data) => {
5+
return { transformed: data };
6+
}],
7+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import axios from 'axios';
2+
3+
// This should be migratable
4+
const todos = await axios.get('https://api.example.com/todos');
5+
6+
// But this one has unsupported transformRequest, so entire file should be skipped
7+
const data = await axios.post('https://api.example.com/data', { foo: 'bar' }, {
8+
transformRequest: [(data) => JSON.stringify(data)],
9+
});
10+
11+
console.log(todos, data);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import axios from 'axios';
2+
3+
const response = await axios.get('https://api.example.com/items', {
4+
headers: { 'Authorization': 'Bearer token' },
5+
});
6+
7+
const created = await axios.request({
8+
url: 'https://api.example.com/create',
9+
method: 'POST',
10+
data: { item: 'test' },
11+
validateStatus: (status) => status < 500,
12+
});
13+
14+
console.log(response, created);

0 commit comments

Comments
 (0)