├── .github ├── dependabot.yaml └── workflows │ └── nodejs.yaml ├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── index.js └── index.test.js └── types.d.ts /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: / 5 | schedule: 6 | interval: daily 7 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yaml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | node-version: [14.x, 16.x] 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Use Node.js ${{ matrix.node-version }} 16 | uses: actions/setup-node@v1 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | - name: npm test 20 | run: npm ci && npm run test 21 | env: 22 | CI: true 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .DS_Store 4 | *.log 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Parmesh Krishen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # object-to-formdata 2 | 3 | > Convenient JavaScript function that serializes Objects to FormData instances. 4 | 5 | [![npm](https://img.shields.io/npm/v/object-to-formdata.svg)](https://www.npmjs.com/package/object-to-formdata) 6 | [![npm](https://img.shields.io/npm/dt/object-to-formdata.svg)](https://www.npmjs.com/package/object-to-formdata) 7 | 8 | ## Install 9 | 10 | ```sh 11 | npm install object-to-formdata 12 | ``` 13 | 14 | ## Usage 15 | 16 | **NOTE: STARTING WITH VERSION 4.0.0, THE NAMED EXPORT HAS CHANGED!** 17 | 18 | **NOTE: STARTING WITH VERSION 3.0.0, THERE IS NO DEFAULT EXPORT!** 19 | 20 | ```js 21 | import { serialize } from 'object-to-formdata'; 22 | 23 | const object = { 24 | /** 25 | * key-value mapping 26 | * values can be primitives or objects 27 | */ 28 | }; 29 | 30 | const options = { 31 | /** 32 | * include array indices in FormData keys 33 | * defaults to false 34 | */ 35 | indices: false, 36 | 37 | /** 38 | * treat null values like undefined values and ignore them 39 | * defaults to false 40 | */ 41 | nullsAsUndefineds: false, 42 | 43 | /** 44 | * convert true or false to 1 or 0 respectively 45 | * defaults to false 46 | */ 47 | booleansAsIntegers: false, 48 | 49 | /** 50 | * store arrays even if they're empty 51 | * defaults to false 52 | */ 53 | allowEmptyArrays: false, 54 | 55 | /** 56 | * don't include array notation in FormData keys for any attributes except Files in arrays 57 | * defaults to false 58 | */ 59 | noAttributesWithArrayNotation: false, 60 | 61 | /** 62 | * don't include array notation in FormData keys for Files in arrays 63 | * defaults to false 64 | */ 65 | noFilesWithArrayNotation: false, 66 | 67 | /** 68 | * use dots instead of brackets for object notation in FormData keys 69 | * defaults to false 70 | */ 71 | dotsForObjectNotation: false, 72 | }; 73 | 74 | const formData = serialize( 75 | object, 76 | options, // optional 77 | existingFormData, // optional 78 | keyPrefix, // optional 79 | ); 80 | 81 | console.log(formData); 82 | ``` 83 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "object-to-formdata", 3 | "version": "4.5.1", 4 | "keywords": [ 5 | "form", 6 | "formdata", 7 | "javascript", 8 | "object", 9 | "object-to-formdata", 10 | "submit" 11 | ], 12 | "repository": "github:therealparmesh/object-to-formdata", 13 | "license": "MIT", 14 | "author": "Parmesh Krishen", 15 | "main": "src/index.js", 16 | "types": "types.d.ts", 17 | "scripts": { 18 | "prepare": "pro-commit", 19 | "pro-commit:task": "pretty-quick --staged", 20 | "test": "jest --coverage" 21 | }, 22 | "prettier": { 23 | "singleQuote": true, 24 | "trailingComma": "all" 25 | }, 26 | "jest": { 27 | "testEnvironment": "jsdom" 28 | }, 29 | "devDependencies": { 30 | "jest": "^27.4.7", 31 | "prettier": "^2.8.1", 32 | "pretty-quick": "^3.1.3", 33 | "pro-commit": "^1.2.3" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | function isUndefined(value) { 2 | return value === undefined; 3 | } 4 | 5 | function isNull(value) { 6 | return value === null; 7 | } 8 | 9 | function isBoolean(value) { 10 | return typeof value === 'boolean'; 11 | } 12 | 13 | function isObject(value) { 14 | return value === Object(value); 15 | } 16 | 17 | function isArray(value) { 18 | return Array.isArray(value); 19 | } 20 | 21 | function isDate(value) { 22 | return value instanceof Date; 23 | } 24 | 25 | function isBlob(value, isReactNative) { 26 | return isReactNative 27 | ? isObject(value) && !isUndefined(value.uri) 28 | : isObject(value) && 29 | typeof value.size === 'number' && 30 | typeof value.type === 'string' && 31 | typeof value.slice === 'function'; 32 | } 33 | 34 | function isFile(value, isReactNative) { 35 | return ( 36 | isBlob(value, isReactNative) && 37 | typeof value.name === 'string' && 38 | (isObject(value.lastModifiedDate) || typeof value.lastModified === 'number') 39 | ); 40 | } 41 | 42 | function initCfg(value) { 43 | return isUndefined(value) ? false : value; 44 | } 45 | 46 | function serialize(obj, cfg, fd, pre) { 47 | cfg = cfg || {}; 48 | fd = fd || new FormData(); 49 | 50 | cfg.indices = initCfg(cfg.indices); 51 | cfg.nullsAsUndefineds = initCfg(cfg.nullsAsUndefineds); 52 | cfg.booleansAsIntegers = initCfg(cfg.booleansAsIntegers); 53 | cfg.allowEmptyArrays = initCfg(cfg.allowEmptyArrays); 54 | cfg.noAttributesWithArrayNotation = initCfg( 55 | cfg.noAttributesWithArrayNotation, 56 | ); 57 | cfg.noFilesWithArrayNotation = initCfg(cfg.noFilesWithArrayNotation); 58 | cfg.dotsForObjectNotation = initCfg(cfg.dotsForObjectNotation); 59 | 60 | const isReactNative = typeof fd.getParts === 'function'; 61 | 62 | if (isUndefined(obj)) { 63 | return fd; 64 | } else if (isNull(obj)) { 65 | if (!cfg.nullsAsUndefineds) { 66 | fd.append(pre, ''); 67 | } 68 | } else if (isBoolean(obj)) { 69 | if (cfg.booleansAsIntegers) { 70 | fd.append(pre, obj ? 1 : 0); 71 | } else { 72 | fd.append(pre, obj); 73 | } 74 | } else if (isArray(obj)) { 75 | if (obj.length) { 76 | obj.forEach((value, index) => { 77 | let key = pre + '[' + (cfg.indices ? index : '') + ']'; 78 | 79 | if ( 80 | cfg.noAttributesWithArrayNotation || 81 | (cfg.noFilesWithArrayNotation && isFile(value, isReactNative)) 82 | ) { 83 | key = pre; 84 | } 85 | 86 | serialize(value, cfg, fd, key); 87 | }); 88 | } else if (cfg.allowEmptyArrays) { 89 | fd.append(cfg.noAttributesWithArrayNotation ? pre : pre + '[]', ''); 90 | } 91 | } else if (isDate(obj)) { 92 | fd.append(pre, obj.toISOString()); 93 | } else if (isObject(obj) && !isBlob(obj, isReactNative)) { 94 | Object.keys(obj).forEach((prop) => { 95 | const value = obj[prop]; 96 | 97 | if (isArray(value)) { 98 | while (prop.length > 2 && prop.lastIndexOf('[]') === prop.length - 2) { 99 | prop = prop.substring(0, prop.length - 2); 100 | } 101 | } 102 | 103 | const key = pre 104 | ? cfg.dotsForObjectNotation 105 | ? pre + '.' + prop 106 | : pre + '[' + prop + ']' 107 | : prop; 108 | 109 | serialize(value, cfg, fd, key); 110 | }); 111 | } else { 112 | fd.append(pre, obj); 113 | } 114 | 115 | return fd; 116 | } 117 | 118 | module.exports = { 119 | serialize, 120 | }; 121 | -------------------------------------------------------------------------------- /src/index.test.js: -------------------------------------------------------------------------------- 1 | const { serialize } = require('.'); 2 | 3 | const formDataAppend = global.FormData.prototype.append; 4 | 5 | beforeEach(() => { 6 | global.FormData.prototype.append = jest.fn(formDataAppend); 7 | }); 8 | 9 | test('undefined', () => { 10 | const formData = serialize({ 11 | foo: undefined, 12 | }); 13 | 14 | expect(formData.append).not.toHaveBeenCalled(); 15 | expect(formData.get('foo')).toBe(null); 16 | }); 17 | 18 | test('null', () => { 19 | const formData = serialize({ 20 | foo: null, 21 | }); 22 | 23 | expect(formData.append).toHaveBeenCalledTimes(1); 24 | expect(formData.append).toHaveBeenCalledWith('foo', ''); 25 | expect(formData.get('foo')).toBe(''); 26 | }); 27 | 28 | test('null with nullsAsUndefineds option', () => { 29 | const formData = serialize( 30 | { 31 | foo: null, 32 | }, 33 | { 34 | nullsAsUndefineds: true, 35 | }, 36 | ); 37 | 38 | expect(formData.append).not.toHaveBeenCalled(); 39 | expect(formData.get('foo')).toBe(null); 40 | }); 41 | 42 | test('boolean', () => { 43 | const formData = serialize({ 44 | foo: true, 45 | bar: false, 46 | }); 47 | 48 | expect(formData.append).toHaveBeenCalledTimes(2); 49 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo', true); 50 | expect(formData.append).toHaveBeenNthCalledWith(2, 'bar', false); 51 | expect(formData.get('foo')).toBe('true'); 52 | expect(formData.get('bar')).toBe('false'); 53 | }); 54 | 55 | test('boolean with booleansAsIntegers option', () => { 56 | const formData = serialize( 57 | { 58 | foo: true, 59 | bar: false, 60 | }, 61 | { 62 | booleansAsIntegers: true, 63 | }, 64 | ); 65 | 66 | expect(formData.append).toHaveBeenCalledTimes(2); 67 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo', 1); 68 | expect(formData.append).toHaveBeenNthCalledWith(2, 'bar', 0); 69 | expect(formData.get('foo')).toBe('1'); 70 | expect(formData.get('bar')).toBe('0'); 71 | }); 72 | 73 | test('integer', () => { 74 | const formData = serialize({ 75 | foo: 1, 76 | }); 77 | 78 | expect(formData.append).toHaveBeenCalledTimes(1); 79 | expect(formData.append).toHaveBeenCalledWith('foo', 1); 80 | expect(formData.get('foo')).toBe('1'); 81 | }); 82 | 83 | test('float', () => { 84 | const formData = serialize({ 85 | foo: 1.01, 86 | }); 87 | 88 | expect(formData.append).toHaveBeenCalledTimes(1); 89 | expect(formData.append).toHaveBeenCalledWith('foo', 1.01); 90 | expect(formData.get('foo')).toBe('1.01'); 91 | }); 92 | 93 | test('string', () => { 94 | const formData = serialize({ 95 | foo: 'bar', 96 | }); 97 | 98 | expect(formData.append).toHaveBeenCalledTimes(1); 99 | expect(formData.append).toHaveBeenCalledWith('foo', 'bar'); 100 | expect(formData.get('foo')).toBe('bar'); 101 | }); 102 | 103 | test('empty string', () => { 104 | const formData = serialize({ 105 | foo: '', 106 | }); 107 | 108 | expect(formData.append).toHaveBeenCalledTimes(1); 109 | expect(formData.append).toHaveBeenCalledWith('foo', ''); 110 | expect(formData.get('foo')).toBe(''); 111 | }); 112 | 113 | test('Object', () => { 114 | const formData = serialize({ 115 | foo: { 116 | bar: 'baz', 117 | qux: 'quux', 118 | }, 119 | }); 120 | 121 | expect(formData.append).toHaveBeenCalledTimes(2); 122 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo[bar]', 'baz'); 123 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo[qux]', 'quux'); 124 | expect(formData.get('foo[bar]')).toBe('baz'); 125 | expect(formData.get('foo[qux]')).toBe('quux'); 126 | }); 127 | 128 | test('empty Object', () => { 129 | const formData = serialize({ 130 | foo: {}, 131 | }); 132 | 133 | expect(formData.append).not.toHaveBeenCalled(); 134 | expect(formData.get('foo')).toBe(null); 135 | }); 136 | 137 | test('Object in Array', () => { 138 | const formData = serialize({ 139 | foo: [ 140 | { 141 | bar: 'baz', 142 | }, 143 | { 144 | qux: 'quux', 145 | }, 146 | ], 147 | }); 148 | 149 | expect(formData.append).toHaveBeenCalledTimes(2); 150 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo[][bar]', 'baz'); 151 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo[][qux]', 'quux'); 152 | expect(formData.get('foo[][bar]')).toBe('baz'); 153 | expect(formData.get('foo[][qux]')).toBe('quux'); 154 | }); 155 | 156 | test('Object in Object', () => { 157 | const formData = serialize({ 158 | foo: { 159 | bar: { 160 | baz: { 161 | qux: 'quux', 162 | }, 163 | }, 164 | }, 165 | }); 166 | 167 | expect(formData.append).toHaveBeenCalledTimes(1); 168 | expect(formData.append).toHaveBeenCalledWith('foo[bar][baz][qux]', 'quux'); 169 | expect(formData.get('foo[bar][baz][qux]')).toBe('quux'); 170 | }); 171 | 172 | test('Object with dotsForObjectNotation option', () => { 173 | const formData = serialize( 174 | { 175 | foo: { 176 | bar: 'baz', 177 | qux: [ 178 | { 179 | quux: 'corge', 180 | }, 181 | ], 182 | }, 183 | }, 184 | { 185 | dotsForObjectNotation: true, 186 | }, 187 | ); 188 | 189 | expect(formData.append).toHaveBeenCalledTimes(2); 190 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo.bar', 'baz'); 191 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo.qux[].quux', 'corge'); 192 | expect(formData.get('foo.bar')).toBe('baz'); 193 | expect(formData.get('foo.qux[].quux')).toBe('corge'); 194 | }); 195 | 196 | test('Array', () => { 197 | const formData = serialize({ 198 | foo: ['bar', 'baz'], 199 | }); 200 | 201 | expect(formData.append).toHaveBeenCalledTimes(2); 202 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo[]', 'bar'); 203 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo[]', 'baz'); 204 | expect(formData.getAll('foo[]')).toEqual(['bar', 'baz']); 205 | }); 206 | 207 | test('Array with noAttributesWithArrayNotation option', () => { 208 | const formData = serialize( 209 | { 210 | foo: ['bar', 'baz'], 211 | }, 212 | { 213 | noAttributesWithArrayNotation: true, 214 | }, 215 | ); 216 | 217 | expect(formData.append).toHaveBeenCalledTimes(2); 218 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo', 'bar'); 219 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo', 'baz'); 220 | expect(formData.getAll('foo')).toEqual(['bar', 'baz']); 221 | }); 222 | 223 | test('empty Array', () => { 224 | const formData = serialize({ 225 | foo: [], 226 | }); 227 | 228 | expect(formData.append).not.toHaveBeenCalled(); 229 | expect(formData.get('foo')).toBe(null); 230 | }); 231 | 232 | test('Array in Array', () => { 233 | const formData = serialize({ 234 | foo: [[['bar', 'baz']]], 235 | }); 236 | 237 | expect(formData.append).toHaveBeenCalledTimes(2); 238 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo[][][]', 'bar'); 239 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo[][][]', 'baz'); 240 | expect(formData.getAll('foo[][][]')).toEqual(['bar', 'baz']); 241 | }); 242 | 243 | test('Array in Array with noAttributesWithArrayNotation option', () => { 244 | const formData = serialize( 245 | { 246 | foo: [[['bar', 'baz']]], 247 | }, 248 | { 249 | noAttributesWithArrayNotation: true, 250 | }, 251 | ); 252 | 253 | expect(formData.append).toHaveBeenCalledTimes(2); 254 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo', 'bar'); 255 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo', 'baz'); 256 | expect(formData.getAll('foo')).toEqual(['bar', 'baz']); 257 | }); 258 | 259 | test('Array in Object', () => { 260 | const formData = serialize({ 261 | foo: { 262 | bar: ['baz', 'qux'], 263 | }, 264 | }); 265 | 266 | expect(formData.append).toHaveBeenCalledTimes(2); 267 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo[bar][]', 'baz'); 268 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo[bar][]', 'qux'); 269 | expect(formData.getAll('foo[bar][]')).toEqual(['baz', 'qux']); 270 | }); 271 | 272 | test('Array where key ends with "[]"', () => { 273 | const formData = serialize({ 274 | 'foo[]': ['bar', 'baz'], 275 | }); 276 | 277 | expect(formData.append).toHaveBeenCalledTimes(2); 278 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo[]', 'bar'); 279 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo[]', 'baz'); 280 | expect(formData.getAll('foo[]')).toEqual(['bar', 'baz']); 281 | }); 282 | 283 | test('Array with indices option', () => { 284 | const formData = serialize( 285 | { 286 | foo: ['bar', 'baz'], 287 | }, 288 | { 289 | indices: true, 290 | }, 291 | ); 292 | 293 | expect(formData.append).toHaveBeenCalledTimes(2); 294 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo[0]', 'bar'); 295 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo[1]', 'baz'); 296 | expect(formData.get('foo[0]')).toBe('bar'); 297 | expect(formData.get('foo[1]')).toBe('baz'); 298 | }); 299 | 300 | test('Array with indices and noAttributesWithArrayNotation option', () => { 301 | const formData = serialize( 302 | { 303 | foo: ['bar', 'baz'], 304 | }, 305 | { 306 | indices: true, 307 | noAttributesWithArrayNotation: true, 308 | }, 309 | ); 310 | 311 | expect(formData.append).toHaveBeenCalledTimes(2); 312 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo', 'bar'); 313 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo', 'baz'); 314 | expect(formData.get('foo')).toBe('bar'); 315 | }); 316 | 317 | test('Array with allowEmptyArrays option', () => { 318 | const formData = serialize( 319 | { 320 | foo: [], 321 | }, 322 | { 323 | allowEmptyArrays: true, 324 | }, 325 | ); 326 | 327 | expect(formData.append).toHaveBeenCalledTimes(1); 328 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo[]', ''); 329 | expect(formData.get('foo[]')).toBe(''); 330 | }); 331 | 332 | test('Array with allowEmptyArrays and noAttributesWithArrayNotation options', () => { 333 | const formData = serialize( 334 | { 335 | foo: [], 336 | }, 337 | { 338 | allowEmptyArrays: true, 339 | noAttributesWithArrayNotation: true, 340 | }, 341 | ); 342 | 343 | expect(formData.append).toHaveBeenCalledTimes(1); 344 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo', ''); 345 | expect(formData.get('foo')).toBe(''); 346 | }); 347 | 348 | test('Date', () => { 349 | const foo = new Date(2000, 0, 1, 1, 1, 1); 350 | const formData = serialize({ 351 | foo, 352 | }); 353 | 354 | expect(formData.append).toHaveBeenCalledTimes(1); 355 | expect(formData.append).toHaveBeenCalledWith('foo', foo.toISOString()); 356 | expect(formData.get('foo')).toBe(foo.toISOString()); 357 | }); 358 | 359 | test('File', () => { 360 | const foo = new File([], ''); 361 | const formData = serialize({ 362 | foo, 363 | }); 364 | 365 | expect(formData.append).toHaveBeenCalledTimes(1); 366 | expect(formData.append).toHaveBeenCalledWith('foo', foo); 367 | expect(formData.get('foo')).toBe(foo); 368 | }); 369 | 370 | test('File with noFilesWithArrayNotation option', () => { 371 | const bar = new File([], ''); 372 | const baz = new File([], ''); 373 | const foo = [bar, baz, 'qux']; 374 | const formData = serialize( 375 | { 376 | foo, 377 | }, 378 | { 379 | noFilesWithArrayNotation: true, 380 | }, 381 | ); 382 | 383 | expect(formData.append).toHaveBeenCalledTimes(3); 384 | expect(formData.append).toHaveBeenNthCalledWith(1, 'foo', bar); 385 | expect(formData.append).toHaveBeenNthCalledWith(2, 'foo', baz); 386 | expect(formData.append).toHaveBeenNthCalledWith(3, 'foo[]', 'qux'); 387 | expect(formData.getAll('foo')).toEqual([bar, baz]); 388 | expect(formData.getAll('foo[]')).toEqual(['qux']); 389 | }); 390 | 391 | test('Blob', () => { 392 | const foo = new Blob([]); 393 | const formData = serialize({ 394 | foo, 395 | }); 396 | 397 | expect(formData.append).toHaveBeenCalledTimes(1); 398 | expect(formData.append).toHaveBeenCalledWith('foo', foo); 399 | expect(formData.get('foo')).toEqual(new File([], '')); 400 | }); 401 | 402 | test('React Native Blob', () => { 403 | global.FormData.prototype.getParts = () => {}; 404 | 405 | const foo = { 406 | uri: 'content://...', 407 | }; 408 | const formData = serialize({ 409 | foo, 410 | }); 411 | 412 | delete global.FormData.prototype.getParts; 413 | 414 | expect(formData.append).toHaveBeenCalledTimes(1); 415 | expect(formData.append).toHaveBeenCalledWith('foo', foo); 416 | expect(formData.get('foo')).toBe('[object Object]'); 417 | }); 418 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | export type Options = { 2 | indices?: boolean; 3 | nullsAsUndefineds?: boolean; 4 | booleansAsIntegers?: boolean; 5 | allowEmptyArrays?: boolean; 6 | noAttributesWithArrayNotation?: boolean; 7 | noFilesWithArrayNotation?: boolean; 8 | dotsForObjectNotation?: boolean; 9 | }; 10 | 11 | export function serialize( 12 | object: T, 13 | options?: Options, 14 | existingFormData?: FormData, 15 | keyPrefix?: string, 16 | ): FormData; 17 | --------------------------------------------------------------------------------