Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d93b76d

Browse files
committedFeb 18, 2025·
refactor: normalize common options for all parsers
1 parent 4a9ed7e commit d93b76d

File tree

7 files changed

+177
-73
lines changed

7 files changed

+177
-73
lines changed
 

‎HISTORY.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
unreleased
2+
=========================
3+
4+
* refactor: normalize common options for all parsers
5+
16
2.1.0 / 2025-02-10
27
=========================
38

@@ -11,7 +16,6 @@
1116
2.0.2 / 2024-10-31
1217
=========================
1318

14-
* extract shared utility functions
1519
* remove `unpipe` package and use native `unpipe()` method
1620

1721
2.0.1 / 2024-09-10

‎lib/types/json.js

+2-17
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@
1212
* @private
1313
*/
1414

15-
var bytes = require('bytes')
1615
var createError = require('http-errors')
1716
var debug = require('debug')('body-parser:json')
1817
var isFinished = require('on-finished').isFinished
1918
var read = require('../read')
2019
var typeis = require('type-is')
21-
var { getCharset, typeChecker } = require('../utils')
20+
var { getCharset, normalizeOptions } = require('../utils')
2221

2322
/**
2423
* Module exports.
@@ -53,24 +52,10 @@ var JSON_SYNTAX_REGEXP = /#+/g
5352

5453
function json (options) {
5554
var opts = options || {}
55+
var { inflate, limit, verify, shouldParse } = normalizeOptions(opts, 'application/json')
5656

57-
var limit = typeof opts.limit !== 'number'
58-
? bytes.parse(opts.limit || '100kb')
59-
: opts.limit
60-
var inflate = opts.inflate !== false
6157
var reviver = opts.reviver
6258
var strict = opts.strict !== false
63-
var type = opts.type || 'application/json'
64-
var verify = opts.verify || false
65-
66-
if (verify !== false && typeof verify !== 'function') {
67-
throw new TypeError('option verify must be function')
68-
}
69-
70-
// create the appropriate type checking function
71-
var shouldParse = typeof type !== 'function'
72-
? typeChecker(type)
73-
: type
7459

7560
function parse (body) {
7661
if (body.length === 0) {

‎lib/types/raw.js

+2-18
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
* Module dependencies.
1111
*/
1212

13-
var bytes = require('bytes')
1413
var debug = require('debug')('body-parser:raw')
1514
var isFinished = require('on-finished').isFinished
1615
var read = require('../read')
1716
var typeis = require('type-is')
18-
var { typeChecker } = require('../utils')
17+
var { normalizeOptions } = require('../utils')
1918

2019
/**
2120
* Module exports.
@@ -33,22 +32,7 @@ module.exports = raw
3332

3433
function raw (options) {
3534
var opts = options || {}
36-
37-
var inflate = opts.inflate !== false
38-
var limit = typeof opts.limit !== 'number'
39-
? bytes.parse(opts.limit || '100kb')
40-
: opts.limit
41-
var type = opts.type || 'application/octet-stream'
42-
var verify = opts.verify || false
43-
44-
if (verify !== false && typeof verify !== 'function') {
45-
throw new TypeError('option verify must be function')
46-
}
47-
48-
// create the appropriate type checking function
49-
var shouldParse = typeof type !== 'function'
50-
? typeChecker(type)
51-
: type
35+
var { inflate, limit, verify, shouldParse } = normalizeOptions(opts, 'application/octet-stream')
5236

5337
function parse (buf) {
5438
return buf

‎lib/types/text.js

+2-17
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
* Module dependencies.
1111
*/
1212

13-
var bytes = require('bytes')
1413
var debug = require('debug')('body-parser:text')
1514
var isFinished = require('on-finished').isFinished
1615
var read = require('../read')
1716
var typeis = require('type-is')
18-
var { getCharset, typeChecker } = require('../utils')
17+
var { getCharset, normalizeOptions } = require('../utils')
1918

2019
/**
2120
* Module exports.
@@ -33,23 +32,9 @@ module.exports = text
3332

3433
function text (options) {
3534
var opts = options || {}
35+
var { inflate, limit, verify, shouldParse } = normalizeOptions(opts, 'text/plain')
3636

3737
var defaultCharset = opts.defaultCharset || 'utf-8'
38-
var inflate = opts.inflate !== false
39-
var limit = typeof opts.limit !== 'number'
40-
? bytes.parse(opts.limit || '100kb')
41-
: opts.limit
42-
var type = opts.type || 'text/plain'
43-
var verify = opts.verify || false
44-
45-
if (verify !== false && typeof verify !== 'function') {
46-
throw new TypeError('option verify must be function')
47-
}
48-
49-
// create the appropriate type checking function
50-
var shouldParse = typeof type !== 'function'
51-
? typeChecker(type)
52-
: type
5338

5439
function parse (buf) {
5540
return buf

‎lib/types/urlencoded.js

+2-17
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@
1212
* @private
1313
*/
1414

15-
var bytes = require('bytes')
1615
var createError = require('http-errors')
1716
var debug = require('debug')('body-parser:urlencoded')
1817
var isFinished = require('on-finished').isFinished
1918
var read = require('../read')
2019
var typeis = require('type-is')
2120
var qs = require('qs')
22-
var { getCharset, typeChecker } = require('../utils')
21+
var { getCharset, normalizeOptions } = require('../utils')
2322

2423
/**
2524
* Module exports.
@@ -37,21 +36,12 @@ module.exports = urlencoded
3736

3837
function urlencoded (options) {
3938
var opts = options || {}
39+
var { inflate, limit, verify, shouldParse } = normalizeOptions(opts, 'application/x-www-form-urlencoded')
4040

4141
var extended = Boolean(opts.extended)
42-
var inflate = opts.inflate !== false
43-
var limit = typeof opts.limit !== 'number'
44-
? bytes.parse(opts.limit || '100kb')
45-
: opts.limit
46-
var type = opts.type || 'application/x-www-form-urlencoded'
47-
var verify = opts.verify || false
4842
var charsetSentinel = opts.charsetSentinel
4943
var interpretNumericEntities = opts.interpretNumericEntities
5044

51-
if (verify !== false && typeof verify !== 'function') {
52-
throw new TypeError('option verify must be function')
53-
}
54-
5545
var defaultCharset = opts.defaultCharset || 'utf-8'
5646
if (defaultCharset !== 'utf-8' && defaultCharset !== 'iso-8859-1') {
5747
throw new TypeError('option defaultCharset must be either utf-8 or iso-8859-1')
@@ -60,11 +50,6 @@ function urlencoded (options) {
6050
// create the appropriate query parser
6151
var queryparse = createQueryParser(opts, extended)
6252

63-
// create the appropriate type checking function
64-
var shouldParse = typeof type !== 'function'
65-
? typeChecker(type)
66-
: type
67-
6853
function parse (body, encoding) {
6954
return body.length
7055
? queryparse(body, encoding)

‎lib/utils.js

+41-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*!
22
* body-parser
3-
* Copyright(c) 2014-2015 Douglas Christopher Wilson
3+
* Copyright(c) Express Contributors
44
* MIT Licensed
55
*/
66

@@ -10,6 +10,7 @@
1010
* Module dependencies.
1111
*/
1212

13+
var bytes = require('bytes')
1314
var contentType = require('content-type')
1415
var typeis = require('type-is')
1516

@@ -19,7 +20,7 @@ var typeis = require('type-is')
1920

2021
module.exports = {
2122
getCharset,
22-
typeChecker
23+
normalizeOptions
2324
}
2425

2526
/**
@@ -32,7 +33,7 @@ module.exports = {
3233
function getCharset (req) {
3334
try {
3435
return (contentType.parse(req).parameters.charset || '').toLowerCase()
35-
} catch (e) {
36+
} catch {
3637
return undefined
3738
}
3839
}
@@ -49,3 +50,40 @@ function typeChecker (type) {
4950
return Boolean(typeis(req, type))
5051
}
5152
}
53+
54+
/**
55+
* Normalizes the common options for all parsers.
56+
*
57+
* @param {object} options options to normalize
58+
* @param {string} defaultType default content type
59+
* @returns {object}
60+
*/
61+
function normalizeOptions (options, defaultType) {
62+
if (!defaultType || typeof defaultType !== 'string') {
63+
// Parsers must define a default content type
64+
throw new TypeError('defaultType must be provided')
65+
}
66+
67+
var inflate = options?.inflate !== false
68+
var limit = typeof options?.limit !== 'number'
69+
? bytes.parse(options?.limit || '100kb')
70+
: options?.limit
71+
var type = options?.type || defaultType
72+
var verify = options?.verify || false
73+
74+
if (verify !== false && typeof verify !== 'function') {
75+
throw new TypeError('option verify must be function')
76+
}
77+
78+
// create the appropriate type checking function
79+
var shouldParse = typeof type !== 'function'
80+
? typeChecker(type)
81+
: type
82+
83+
return {
84+
inflate,
85+
limit,
86+
verify,
87+
shouldParse
88+
}
89+
}

‎test/utils.js

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
'use strict'
2+
3+
const assert = require('node:assert')
4+
const { normalizeOptions } = require('../lib/utils.js')
5+
6+
describe('normalizeOptions(options, defaultType)', () => {
7+
it('should return default options when no options are provided', () => {
8+
for (const options of [undefined, null, {}]) {
9+
const result = normalizeOptions(options, 'application/json')
10+
assert.strictEqual(result.inflate, true)
11+
assert.strictEqual(result.limit, 100 * 1024) // 100kb in bytes
12+
assert.strictEqual(result.verify, false)
13+
assert.strictEqual(typeof result.shouldParse, 'function')
14+
}
15+
})
16+
17+
it('should override default options with provided options', () => {
18+
const options = {
19+
inflate: false,
20+
limit: '200kb',
21+
type: 'application/xml',
22+
verify: () => {}
23+
}
24+
const result = normalizeOptions(options, 'application/json')
25+
assert.strictEqual(result.inflate, false)
26+
assert.strictEqual(result.limit, 200 * 1024) // 200kb in bytes
27+
assert.strictEqual(result.verify, options.verify)
28+
assert.strictEqual(typeof result.shouldParse, 'function')
29+
})
30+
31+
it('should remove additional options', () => {
32+
const options = {
33+
inflate: false,
34+
limit: '200kb',
35+
type: 'application/xml',
36+
verify: () => {},
37+
additional: 'option',
38+
something: 'weird'
39+
}
40+
const result = normalizeOptions(options, 'application/json')
41+
assert.strictEqual(result.inflate, false)
42+
assert.strictEqual(result.limit, 200 * 1024) // 200kb in bytes
43+
assert.strictEqual(result.verify, options.verify)
44+
assert.strictEqual(typeof result.shouldParse, 'function')
45+
assert.strictEqual(result.additional, undefined)
46+
assert.strictEqual(result.something, undefined)
47+
})
48+
49+
describe('options', () => {
50+
describe('verify', () => {
51+
it('should throw an error if verify is not a function', () => {
52+
assert.throws(() => {
53+
normalizeOptions({ verify: 'not a function' }, 'application/json')
54+
}, /option verify must be function/)
55+
})
56+
57+
it('should accept a verify function', () => {
58+
const verify = () => {}
59+
const result = normalizeOptions({ verify }, 'application/json')
60+
assert.strictEqual(result.verify, verify)
61+
})
62+
})
63+
64+
describe('limit', () => {
65+
it('should return the default limit if limit is not provided', () => {
66+
const result = normalizeOptions({}, 'application/json')
67+
assert.strictEqual(result.limit, 100 * 1024) // 100kb in bytes
68+
})
69+
70+
it('should accept a number limit', () => {
71+
const result = normalizeOptions({ limit: 1234 }, 'application/json')
72+
assert.strictEqual(result.limit, 1234)
73+
})
74+
75+
it('should parse a string limit', () => {
76+
const result = normalizeOptions({ limit: '200kb' }, 'application/json')
77+
assert.strictEqual(result.limit, 200 * 1024) // 200kb in bytes
78+
})
79+
80+
it('should return null for an invalid limit', () => {
81+
const result = normalizeOptions({ limit: 'invalid' }, 'application/json')
82+
assert.strictEqual(result.limit, null)
83+
})
84+
})
85+
86+
describe('type', () => {
87+
it('should return the default type if type is not provided', () => {
88+
const result = normalizeOptions({}, 'application/json')
89+
assert.strictEqual(result.shouldParse({ headers: { 'content-type': 'application/json', 'content-length': '1024' } }), true)
90+
assert.strictEqual(result.shouldParse({ headers: { 'content-type': 'application/xml', 'content-length': '1024' } }), false)
91+
})
92+
93+
it('should accept a string type', () => {
94+
const result = normalizeOptions({ type: 'application/xml' }, 'application/json')
95+
assert.strictEqual(result.shouldParse({ headers: { 'content-type': 'application/xml', 'content-length': '1024' } }), true)
96+
assert.strictEqual(result.shouldParse({ headers: { 'content-type': 'application/json', 'content-length': '1024' } }), false)
97+
})
98+
99+
it('should accept a type checking function', () => {
100+
const result = normalizeOptions({ type: () => true }, 'application/json')
101+
assert.strictEqual(result.shouldParse({ headers: { 'content-type': 'application/xml' } }), true)
102+
assert.strictEqual(result.shouldParse({ headers: { 'content-type': 'application/json' } }), true)
103+
})
104+
})
105+
})
106+
107+
describe('defaultType', () => {
108+
it('should throw an error if defaultType is not provided', () => {
109+
assert.throws(() => {
110+
normalizeOptions({})
111+
}, /defaultType must be provided/)
112+
assert.throws(() => {
113+
normalizeOptions({}, undefined)
114+
}, /defaultType must be provided/)
115+
})
116+
117+
it('should throw an error if defaultType is not a string', () => {
118+
assert.throws(() => {
119+
normalizeOptions({}, 123)
120+
}, /defaultType must be provided/)
121+
})
122+
})
123+
})

0 commit comments

Comments
 (0)
Please sign in to comment.