UNPKG

axios-case-converter

Version:

Axios transformer/interceptor that converts snake_case/camelCase

157 lines (148 loc) 4.92 kB
import { camelCase as camelCaseString } from 'camel-case'; import { snakeCase as snakeCaseString } from 'snake-case'; import { headerCase as headerCaseString } from 'header-case'; import { applyCaseOptions, preserveSpecificKeys } from './decorators'; import { isFormData, isTransformable, isURLSearchParams } from './util'; import { CaseFunction, CaseFunctions, CaseFunctionTypes, CreateObjectTransformer, CreateObjectTransformerOf, CreateObjectTransformers, ObjectTransformerOptions, ObjectTransformers, Transformable, } from './types'; const caseFunctions: CaseFunctions = { snake: snakeCaseString, camel: camelCaseString, header: headerCaseString, }; const transformObjectUsingCallbackRecursive = ( data: unknown, fn: CaseFunction, overwrite: ObjectTransformerOptions['overwrite'] ): unknown => { if (!isTransformable(data)) { return data; } /* eslint-disable no-console */ // Check FormData/URLSearchParams compatibility if ( (isFormData(data) || isURLSearchParams(data)) && (!data.entries || (overwrite && !data.delete)) ) { const type = isFormData(data) ? 'FormData' : 'URLSearchParams'; const polyfill = isFormData(data) ? 'https://github.com/jimmywarting/FormData' : 'https://github.com/jerrybendy/url-search-params-polyfill'; if ( typeof navigator !== 'undefined' && navigator.product === 'ReactNative' ) { // You cannot transform FormData/URLSearchParams on React Native console.warn( `Be careful that ${type} cannot be transformed on React Native. If you intentionally implemented, ignore this kind of warning: https://facebook.github.io/react-native/docs/debugging.html` ); } else { if (!data.entries) { // You need to polyfill `entries` method console.warn( `You must use polyfill of ${type}.prototype.entries() on Internet Explorer or Safari: ${polyfill}` ); } if (overwrite && !data.delete) { // You need to polyfill `delete` method for overwriting console.warn( `You must use polyfill of ${type}.prototype.delete() on Internet Explorer or Safari: ${polyfill}` ); } } return data; } /* eslint-enable no-console */ const prototype = Object.getPrototypeOf(data); // Storage of new values. // New instances are created when overwriting is disabled. const store: Transformable = overwrite ? data : !prototype ? Object.create(null) : new prototype.constructor(); // We need to clean up all entries before overwriting. let series: | Iterable<[unknown, unknown]> | IterableIterator<[unknown, unknown]>; if (isFormData(data) || isURLSearchParams(data)) { // Create native iterator from FormData/URLSearchParams series = data.entries(); if (overwrite) { // When overwriting, native iterator needs to be copied as array. series = [...series]; for (const [key] of series) { data.delete(key as string); } } } else { // Create array from objects series = Object.entries(data); // Array keys never change, so we don't need to clean up if (overwrite && !Array.isArray(data)) { for (const [key] of series) { delete data[key as string]; } } } for (const [key, value] of series) { if (isFormData(store) || isURLSearchParams(store)) { store.append(fn(key as string), value as string & File); } else if (key !== '__proto__') { store[Array.isArray(data) ? Number(key) : fn(`${key}`)] = transformObjectUsingCallbackRecursive(value, fn, overwrite); } } return store; }; const transformObjectUsingCallback = ( data: unknown, fn: CaseFunction, options?: ObjectTransformerOptions ): unknown => { fn = applyCaseOptions(fn, { stripRegexp: /[^A-Z0-9[\]]+/gi, ...options?.caseOptions, }); if (options?.preservedKeys) { fn = preserveSpecificKeys(fn, options.preservedKeys); } return transformObjectUsingCallbackRecursive( data, fn, options?.overwrite || false ); }; export const createObjectTransformer: CreateObjectTransformer = (fn) => { return (data, options): ReturnType<ReturnType<CreateObjectTransformer>> => { return transformObjectUsingCallback(data, fn, options); }; }; export const createObjectTransformerOf: CreateObjectTransformerOf = ( functionName, options ) => { return createObjectTransformer( options?.[functionName] || caseFunctions[functionName] ); }; export const createObjectTransformers: CreateObjectTransformers = (options) => { const functionNames = Object.keys(caseFunctions) as CaseFunctionTypes[]; const objectTransformers = {} as ObjectTransformers; for (const functionName of functionNames) { objectTransformers[functionName] = createObjectTransformerOf( functionName, options ); } return objectTransformers; };