Skip to content

Commit efa20b0

Browse files
authored
feat: Url search params and remove multiple (#730)
1 parent bd5a7be commit efa20b0

11 files changed

+47
-50
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
### Unreleased 3.0
44

5+
* feat: remove options.multiples ([730](https://github.com/node-formidable/formidable/pull/730))
6+
* use modern URLSearchParams https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams internally
7+
* files and fields values are always arrays
8+
* fields with [] in the name do not receive special treatment
9+
* remove unused qs and querystring dependency
510
* feat: Use ES modules ([727](https://github.com/node-formidable/formidable/pull/727))
611
* options.enabledPlugins must contain the plugin themselves instead of the plugins names
712

examples/with-http.js

+7
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ const server = http.createServer((req, res) => {
4949
<div>File: <input type="file" name="multipleFiles" multiple="multiple" /></div>
5050
<input type="submit" value="Upload" />
5151
</form>
52+
53+
<form action="/api/upload" enctype="multipart/form-data" method="post">
54+
<div>Text field title: <input type="text" name="title" /></div>
55+
<div>Text field with same name: <input type="text" name="title" /></div>
56+
<div>Other field <input type="text" name="other" /></div>
57+
<input type="submit" value="submit simple" />
58+
</form>
5259
`);
5360
});
5461

package.json

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "formidable",
3-
"version": "3.0.0-canary.20210427",
3+
"version": "3.0.0-canary.20210428",
44
"license": "MIT",
55
"description": "A node.js module for parsing form data, especially file uploads.",
66
"homepage": "https://github.com/node-formidable/formidable",
@@ -30,13 +30,12 @@
3030
"pretest": "del-cli ./test/tmp && make-dir ./test/tmp",
3131
"test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --coverage",
3232
"pretest:ci": "yarn run pretest",
33-
"test:ci": "node --experimental-vm-modules node_modules/.bin/nyc jest --coverage"
33+
"test:ci": "node --experimental-vm-modules node_modules/.bin/nyc jest --coverage"
3434
},
3535
"dependencies": {
3636
"dezalgo": "1.0.3",
3737
"hexoid": "1.0.0",
38-
"once": "1.4.0",
39-
"qs": "6.9.3"
38+
"once": "1.4.0"
4039
},
4140
"devDependencies": {
4241
"@commitlint/cli": "8.3.5",

src/Formidable.js

+11-24
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
/* eslint-disable class-methods-use-this */
22
/* eslint-disable no-underscore-dangle */
33

4-
'use strict';
5-
64
import os from 'os';
75
import path from 'path';
86
import hexoid from 'hexoid';
97
import once from 'once';
108
import dezalgo from 'dezalgo';
119
import { EventEmitter } from 'events';
1210
import { StringDecoder } from 'string_decoder';
13-
import { stringify, parse as __parse } from 'qs';
1411
import {
1512
octetstream,
1613
querystring,
@@ -29,7 +26,6 @@ const DEFAULT_OPTIONS = {
2926
encoding: 'utf-8',
3027
hashAlgorithm: false,
3128
uploadDir: os.tmpdir(),
32-
multiples: false,
3329
enabledPlugins: [
3430
octetstream,
3531
querystring,
@@ -156,39 +152,30 @@ class IncomingForm extends EventEmitter {
156152

157153
this.on('field', (name, value) => {
158154
if (
159-
this.options.multiples &&
160-
(this.type === 'multipart' || this.type === 'urlencoded')
155+
this.type === 'multipart' || this.type === 'urlencoded'
161156
) {
162-
const mObj = { [name]: value };
163-
mockFields = mockFields
164-
? `${mockFields}&${stringify(mObj)}`
165-
: `${stringify(mObj)}`;
157+
if (!hasOwnProp(fields, name)) {
158+
fields[name] = [value];
159+
} else {
160+
fields[name].push(value);
161+
}
162+
166163
} else {
167164
fields[name] = value;
168165
}
169166
});
170167
this.on('file', (name, file) => {
171-
// TODO: too much nesting
172-
if (this.options.multiples) {
173-
if (hasOwnProp(files, name)) {
174-
if (!Array.isArray(files[name])) {
175-
files[name] = [files[name]];
176-
}
177-
files[name].push(file);
168+
if (!hasOwnProp(files, name)) {
169+
files[name] = [file];
178170
} else {
179-
files[name] = file;
171+
files[name].push(file);
180172
}
181-
} else {
182-
files[name] = file;
183-
}
173+
184174
});
185175
this.on('error', (err) => {
186176
callback(err, fields, files);
187177
});
188178
this.on('end', () => {
189-
if (this.options.multiples) {
190-
Object.assign(fields, __parse(mockFields));
191-
}
192179
callback(null, fields, files);
193180
});
194181
}

src/parsers/Querystring.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable no-underscore-dangle */
22

33
import { Transform } from 'stream';
4-
import { parse } from 'querystring';
4+
55

66
// This is a buffering parser, not quite as nice as the multipart one.
77
// If I find time I'll rewrite this to be fully streaming as well
@@ -20,12 +20,11 @@ class QuerystringParser extends Transform {
2020
}
2121

2222
_flush(callback) {
23-
const fields = parse(this.buffer, '&', '=');
24-
// eslint-disable-next-line no-restricted-syntax, guard-for-in
25-
for (const key in fields) {
23+
const fields = new URLSearchParams(this.buffer);
24+
for (const [key, value] of fields) {
2625
this.push({
2726
key,
28-
value: fields[key],
27+
value,
2928
});
3029
}
3130
this.buffer = '';

test/integration/file-write-stream-handler-option.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ test('file write stream handler', (done) => {
4646

4747
form.parse(req, (err, fields, files) => {
4848
strictEqual(Object.keys(files).length, 1);
49-
const { file } = files;
49+
const file = files.file[0];
5050

5151
strictEqual(file.size, 301);
5252
strictEqual(typeof file.filepath, 'string');

test/integration/octet-stream.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ test('octet stream', (done) => {
2323

2424
form.parse(req, (err, fields, files) => {
2525
strictEqual(Object.keys(files).length, 1);
26-
const { file } = files;
26+
const file = files.file[0];
2727

2828
strictEqual(file.size, 301);
2929

test/integration/store-files-option.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ test('store files option', (done) => {
3535

3636
form.parse(req, (err, fields, files) => {
3737
strictEqual(Object.keys(files).length, 1);
38-
const { file } = files;
38+
const file = files.file[0];
3939

4040
strictEqual(file.size, 301);
4141
strictEqual(typeof file.filepath, 'string');

test/standalone/issue-46.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ test('issue 46', (done) => {
4747
const obj = JSON.parse(body);
4848

4949
ok(obj.fields.foo, 'should have fields.foo === barry');
50-
strictEqual(obj.fields.foo, 'barry');
50+
strictEqual(obj.fields.foo[0], 'barry');
5151

5252
server.close();
5353
done();

test/unit/formidable.test.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ function makeHeader(originalFilename) {
109109
expect(fields.a[0]).toBe('1');
110110
expect(fields.a[1]).toBe('2');
111111
});
112-
form.emit('field', 'a[]', '1');
113-
form.emit('field', 'a[]', '2');
112+
form.emit('field', 'a', '1');
113+
form.emit('field', 'a', '2');
114114
form.emit('end');
115115
});
116116

@@ -123,14 +123,14 @@ function makeHeader(originalFilename) {
123123
'content-type': 'multipart/form-data; boundary=----TLVx',
124124
};
125125
form.parse(req, (error, fields) => {
126-
expect(Array.isArray(fields.a)).toBe(true);
127-
expect(fields.a[0][0]).toBe('a');
128-
expect(fields.a[0][1]).toBe('b');
129-
expect(fields.a[1][0]).toBe('c');
126+
expect(Array.isArray(fields[`a[0]`])).toBe(true);
127+
expect(fields[`a[0]`][0]).toBe('a');
128+
expect(fields[`a[0]`][1]).toBe('b');
129+
expect(fields[`a[1]`][0]).toBe('c');
130130
});
131-
form.emit('field', 'a[0][]', 'a');
132-
form.emit('field', 'a[0][]', 'b');
133-
form.emit('field', 'a[1][]', 'c');
131+
form.emit('field', 'a[0]', 'a');
132+
form.emit('field', 'a[0]', 'b');
133+
form.emit('field', 'a[1]', 'c');
134134
form.emit('end');
135135
});
136136

@@ -143,15 +143,15 @@ function makeHeader(originalFilename) {
143143
'content-type': 'multipart/form-data; boundary=----TLVx',
144144
};
145145
form.parse(req, (error, fields) => {
146-
expect(fields.a.x).toBe('1');
147-
expect(fields.a.y).toBe('2');
146+
expect(fields[`a[x]`][0]).toBe('1');
147+
expect(fields[`a[y]`][0]).toBe('2');
148148
});
149149
form.emit('field', 'a[x]', '1');
150150
form.emit('field', 'a[y]', '2');
151151
form.emit('end');
152152
});
153153

154-
test(`${name}#_Nested object parameters support`, () => {
154+
xtest(`${name}#_Nested object parameters support`, () => {
155155
const form = getForm(name, { multiples: true });
156156

157157
const req = new http.ClientRequest();

yarn.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -4923,7 +4923,7 @@ [email protected]:
49234923
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
49244924
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
49254925

4926-
qs@6.9.3, qs@^6.5.1:
4926+
qs@^6.5.1:
49274927
version "6.9.3"
49284928
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e"
49294929
integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==

0 commit comments

Comments
 (0)