From f047c9d527c329017d3d94ccbb146e6de4cff75c Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 9 Jan 2022 22:40:45 -0800 Subject: [PATCH] [Dev Deps] backport from main --- .editorconfig | 45 +++++++++++++++++++++ .eslintignore | 1 - .eslintrc | 32 +++++++++++---- .gitignore | 8 ++++ .npmignore | 18 +++++++-- .nycrc | 13 ++++++ README.md | 2 +- bower.json | 38 ++++++++--------- component.json | 26 ++++++------ lib/parse.js | 15 +++---- lib/stringify.js | 49 +++++++++++++++++++--- lib/utils.js | 20 +++++---- package.json | 101 ++++++++++++++++++++++++---------------------- test/index.js | 2 + test/parse.js | 54 ++++++++++++------------- test/stringify.js | 2 +- 16 files changed, 280 insertions(+), 146 deletions(-) create mode 100644 .editorconfig delete mode 100644 .eslintignore create mode 100644 .nycrc diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..cd3439f5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,45 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 160 +quote_type = single + +[test/*] +max_line_length = off + +[*.md] +indent_size = off +max_line_length = off + +[*.json] +max_line_length = off + +[Makefile] +max_line_length = off + +[CHANGELOG.md] +indent_style = space +indent_size = 2 + +[LICENSE] +indent_size = 2 +max_line_length = off + +[coverage/**/*] +indent_size = off +indent_style = off +indent = off +max_line_length = off + +[dist/*] +max_line_length = off +insert_final_newline = off + +[.nycrc] +indent_style = off diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 1521c8b7..00000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -dist diff --git a/.eslintrc b/.eslintrc index 16344a23..20ed377f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -3,21 +3,37 @@ "extends": "@ljharb", + "ignorePatterns": [ + "dist/", + ], + "rules": { - "complexity": [2, 25], - "consistent-return": [1], + "complexity": [2, 29], + "consistent-return": 1, "func-name-matching": 0, "id-length": [2, { "min": 1, "max": 25, "properties": "never" }], "indent": [2, 4], - "max-len": 0, "max-lines-per-function": 0, - "max-params": [2, 9], - "max-statements": [0, 36], - "no-extra-parens": [1], - "no-continue": [1], + "max-lines": 0, + "max-params": [2, 12], + "max-statements": [2, 45], + "multiline-comment-style": 0, + "no-continue": 1, "no-magic-numbers": 0, + "no-param-reassign": 1, "no-restricted-syntax": [2, "BreakStatement", "DebuggerStatement", "ForInStatement", "LabeledStatement", "WithStatement"], - "operator-linebreak": 1, "sort-keys": 0, }, + + "overrides": [ + { + "files": "test/**", + "rules": { + "max-lines-per-function": 0, + "max-statements": 0, + "no-extend-native": 0, + "function-paren-newline": 0, + }, + }, + ], } diff --git a/.gitignore b/.gitignore index 8cace31c..267da50e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,11 @@ lib-cov complexity.md tests.tap dist/* + +# Only apps should have lockfiles +yarn.lock +package-lock.json +npm-shrinkwrap.json + +.nyc_output/ +coverage/ diff --git a/.npmignore b/.npmignore index ac980d91..5fafe6be 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,14 @@ -bower.json -component.json -.npmignore -.travis.yml +# gitignore +npm-debug.log +node_modules +.DS_Store + +# Only apps should have lockfiles +yarn.lock +package-lock.json +npm-shrinkwrap.json + +.nyc_output/ +coverage/ + +.github/workflows diff --git a/.nycrc b/.nycrc new file mode 100644 index 00000000..1d57cabe --- /dev/null +++ b/.nycrc @@ -0,0 +1,13 @@ +{ + "all": true, + "check-coverage": false, + "reporter": ["text-summary", "text", "html", "json"], + "lines": 86, + "statements": 85.93, + "functions": 82.43, + "branches": 76.06, + "exclude": [ + "coverage", + "dist" + ] +} diff --git a/README.md b/README.md index e393c936..9a1c16af 100644 --- a/README.md +++ b/README.md @@ -380,7 +380,7 @@ assert.equal(nullsSkipped, 'a=b'); ### Dealing with special character sets -By default the encoding and decoding of characters is done in `utf-8`. If you +By default the encoding and decoding of characters is done in `utf-8`. If you wish to encode querystrings to a different character set (i.e. [Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS)) you can use the [`qs-iconv`](https://github.com/martinheidegger/qs-iconv) library: diff --git a/bower.json b/bower.json index 44f05064..7a582762 100644 --- a/bower.json +++ b/bower.json @@ -1,21 +1,21 @@ { - "name": "qs", - "main": "dist/qs.js", - "homepage": "https://github.com/hapijs/qs", - "authors": [ - "Nathan LaFreniere " - ], - "description": "A querystring parser that supports nesting and arrays, with a depth limit", - "keywords": [ - "querystring", - "qs" - ], - "license": "BSD-3-Clause", - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ] + "name": "qs", + "main": "dist/qs.js", + "homepage": "https://github.com/hapijs/qs", + "authors": [ + "Nathan LaFreniere " + ], + "description": "A querystring parser that supports nesting and arrays, with a depth limit", + "keywords": [ + "querystring", + "qs" + ], + "license": "BSD-3-Clause", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] } diff --git a/component.json b/component.json index 971bbb27..c1715a89 100644 --- a/component.json +++ b/component.json @@ -1,15 +1,15 @@ { - "name": "qs", - "repository": "hapijs/qs", - "description": "query-string parser / stringifier with nesting support", - "version": "6.2.3", - "keywords": ["querystring", "query", "parser"], - "main": "lib/index.js", - "scripts": [ - "lib/index.js", - "lib/parse.js", - "lib/stringify.js", - "lib/utils.js" - ], - "license": "BSD-3-Clause" + "name": "qs", + "repository": "hapijs/qs", + "description": "query-string parser / stringifier with nesting support", + "version": "6.2.3", + "keywords": ["querystring", "query", "parser"], + "main": "lib/index.js", + "scripts": [ + "lib/index.js", + "lib/parse.js", + "lib/stringify.js", + "lib/utils.js" + ], + "license": "BSD-3-Clause" } diff --git a/lib/parse.js b/lib/parse.js index 2110e74a..7ef09a85 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -60,11 +60,11 @@ var parseObject = function parseObject(chain, val, options) { if (!options.parseArrays && cleanRoot === '') { obj = { 0: val }; } else if ( - !isNaN(index) && - root !== cleanRoot && - String(index) === cleanRoot && - index >= 0 && - (options.parseArrays && index <= options.arrayLimit) + !isNaN(index) + && root !== cleanRoot + && String(index) === cleanRoot + && index >= 0 + && (options.parseArrays && index <= options.arrayLimit) ) { obj = []; obj[index] = parseObject(chain, val, options); @@ -98,10 +98,7 @@ var parseKeys = function parseKeys(givenKey, val, options) { var keys = []; if (parent) { - /* - * If we aren't using plain objects, optionally prefix keys - * that would overwrite object prototype properties - */ + // If we aren't using plain objects, optionally prefix keys that would overwrite object prototype properties if (!options.plainObjects && has.call(Object.prototype, parent)) { if (!options.allowPrototypes) { return; diff --git a/lib/stringify.js b/lib/stringify.js index 34b977b2..b5009706 100644 --- a/lib/stringify.js +++ b/lib/stringify.js @@ -23,8 +23,17 @@ var defaults = { }; var isArray = Array.isArray; - -var stringify = function stringify(object, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots) { +var stringify = function stringify( + object, + prefix, + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots +) { var obj = object; if (typeof filter === 'function') { obj = filter(prefix, obj); @@ -67,9 +76,29 @@ var stringify = function stringify(object, prefix, generateArrayPrefix, strictNu } if (isArray(obj)) { - values = values.concat(stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots)); + values = values.concat(stringify( + obj[key], + generateArrayPrefix(prefix, key), + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots + )); } else { - values = values.concat(stringify(obj[key], prefix + (allowDots ? '.' + key : '[' + key + ']'), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots)); + values = values.concat(stringify( + obj[key], + prefix + (allowDots ? '.' + key : '[' + key + ']'), + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots + )); } } @@ -133,7 +162,17 @@ module.exports = function (object, opts) { continue; } - keys = keys.concat(stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots)); + keys = keys.concat(stringify( + obj[key], + key, + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots + )); } return keys.join(delimiter); diff --git a/lib/utils.js b/lib/utils.js index f778daae..d445a74f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -114,13 +114,13 @@ exports.encode = function (str) { var c = string.charCodeAt(i); if ( - c === 0x2D || // - - c === 0x2E || // . - c === 0x5F || // _ - c === 0x7E || // ~ - (c >= 0x30 && c <= 0x39) || // 0-9 - (c >= 0x41 && c <= 0x5A) || // a-z - (c >= 0x61 && c <= 0x7A) // A-Z + c === 0x2D // - + || c === 0x2E // . + || c === 0x5F // _ + || c === 0x7E // ~ + || (c >= 0x30 && c <= 0x39) // 0-9 + || (c >= 0x41 && c <= 0x5A) // a-z + || (c >= 0x61 && c <= 0x7A) // A-Z ) { out += string.charAt(i); continue; @@ -143,7 +143,11 @@ exports.encode = function (str) { i += 1; c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF)); - out += hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]; + /* eslint operator-linebreak: [2, "before"] */ + out += hexTable[0xF0 | (c >> 18)] + + hexTable[0x80 | ((c >> 12) & 0x3F)] + + hexTable[0x80 | ((c >> 6) & 0x3F)] + + hexTable[0x80 | (c & 0x3F)]; } return out; diff --git a/package.json b/package.json index 4bf439f8..b248fb9d 100644 --- a/package.json +++ b/package.json @@ -1,51 +1,54 @@ { - "name": "qs", - "description": "A querystring parser that supports nesting and arrays, with a depth limit", - "homepage": "https://github.com/ljharb/qs", - "version": "6.2.3", - "repository": { - "type": "git", - "url": "https://github.com/ljharb/qs.git" - }, - "main": "lib/index.js", - "contributors": [ - { - "name": "Jordan Harband", - "email": "ljharb@gmail.com", - "url": "http://ljharb.codes" - } - ], - "keywords": [ - "querystring", - "qs" - ], - "engines": { - "node": ">=0.6" - }, - "dependencies": {}, - "devDependencies": { - "@ljharb/eslint-config": "^6.0.0", - "browserify": "^13.0.1", - "covert": "^1.1.0", - "eslint": "^3.1.0", - "evalmd": "^0.0.17", - "iconv-lite": "^0.4.13", - "mkdirp": "^0.5.1", - "parallelshell": "^2.0.0", - "qs-iconv": "^1.0.4", - "safe-publish-latest": "^1.1.1", - "safer-buffer": "^2.0.2", - "tape": "^4.6.3" - }, - "scripts": { - "pretest": "npm run --silent readme && npm run --silent lint", - "test": "npm run --silent coverage", - "tests-only": "node test", - "readme": "evalmd README.md", - "lint": "eslint lib/*.js text/*.js", - "coverage": "covert test", - "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js", - "prepublish": "npm run dist" - }, - "license": "BSD-3-Clause" + "name": "qs", + "description": "A querystring parser that supports nesting and arrays, with a depth limit", + "homepage": "https://github.com/ljharb/qs", + "version": "6.2.3", + "repository": { + "type": "git", + "url": "https://github.com/ljharb/qs.git" + }, + "main": "lib/index.js", + "contributors": [ + { + "name": "Jordan Harband", + "email": "ljharb@gmail.com", + "url": "http://ljharb.codes" + } + ], + "keywords": [ + "querystring", + "qs" + ], + "engines": { + "node": ">=0.6" + }, + "devDependencies": { + "@ljharb/eslint-config": "^20.1.0", + "aud": "^1.1.5", + "browserify": "^16.5.2", + "eclint": "^2.8.1", + "eslint": "^8.6.0", + "evalmd": "^0.0.17", + "iconv-lite": "^0.4.24", + "in-publish": "^2.0.1", + "mkdirp": "^0.5.1", + "nyc": "^10.3.2", + "qs-iconv": "^1.0.4", + "safe-publish-latest": "^2.0.0", + "safer-buffer": "^2.1.2", + "tape": "^5.4.0" + }, + "scripts": { + "prepublishOnly": "safe-publish-latest && npm run dist", + "prepublish": "not-in-publish || npm run prepublishOnly", + "pretest": "npm run --silent readme && npm run --silent lint", + "test": "npm run --silent tests-only", + "tests-only": "nyc tape 'test/**/*.js'", + "posttest": "aud --production", + "readme": "evalmd README.md", + "postlint": "eclint check $(git ls-files | xargs find 2> /dev/null | grep -vE 'node_modules|\\.git')", + "lint": "eslint --ext=js,mjs .", + "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js" + }, + "license": "BSD-3-Clause" } diff --git a/test/index.js b/test/index.js index b6a7d952..5e6bc8fb 100644 --- a/test/index.js +++ b/test/index.js @@ -1,3 +1,5 @@ +'use strict'; + require('./parse'); require('./stringify'); diff --git a/test/parse.js b/test/parse.js index f6051312..87e09707 100644 --- a/test/parse.js +++ b/test/parse.js @@ -7,7 +7,7 @@ var SaferBuffer = require('safer-buffer').Buffer; test('parse()', function (t) { t.test('parses a simple string', function (st) { - st.deepEqual(qs.parse('0=foo'), { '0': 'foo' }); + st.deepEqual(qs.parse('0=foo'), { 0: 'foo' }); st.deepEqual(qs.parse('foo=c++'), { foo: 'c ' }); st.deepEqual(qs.parse('a[>=]=23'), { a: { '>=': '23' } }); st.deepEqual(qs.parse('a[<=>]==23'), { a: { '<=>': '=23' } }); @@ -85,7 +85,7 @@ test('parse()', function (t) { t.test('limits specific array indices to 20', function (st) { st.deepEqual(qs.parse('a[20]=a'), { a: ['a'] }); - st.deepEqual(qs.parse('a[21]=a'), { a: { '21': 'a' } }); + st.deepEqual(qs.parse('a[21]=a'), { a: { 21: 'a' } }); st.end(); }); @@ -116,11 +116,11 @@ test('parse()', function (t) { }); t.test('transforms arrays to objects', function (st) { - st.deepEqual(qs.parse('foo[0]=bar&foo[bad]=baz'), { foo: { '0': 'bar', bad: 'baz' } }); - st.deepEqual(qs.parse('foo[bad]=baz&foo[0]=bar'), { foo: { bad: 'baz', '0': 'bar' } }); - st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar'), { foo: { bad: 'baz', '0': 'bar' } }); - st.deepEqual(qs.parse('foo[]=bar&foo[bad]=baz'), { foo: { '0': 'bar', bad: 'baz' } }); - st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo'), { foo: { bad: 'baz', '0': 'bar', '1': 'foo' } }); + st.deepEqual(qs.parse('foo[0]=bar&foo[bad]=baz'), { foo: { 0: 'bar', bad: 'baz' } }); + st.deepEqual(qs.parse('foo[bad]=baz&foo[0]=bar'), { foo: { bad: 'baz', 0: 'bar' } }); + st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar'), { foo: { bad: 'baz', 0: 'bar' } }); + st.deepEqual(qs.parse('foo[]=bar&foo[bad]=baz'), { foo: { 0: 'bar', bad: 'baz' } }); + st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo'), { foo: { bad: 'baz', 0: 'bar', 1: 'foo' } }); st.deepEqual(qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb'), { foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] }); st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: false }), { a: { 0: 'b', t: 'u' } }); @@ -136,16 +136,16 @@ test('parse()', function (t) { st.deepEqual(qs.parse('foo[0][0].baz=bar&fool.bad=baz', { allowDots: true }), { foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } }); st.deepEqual(qs.parse('foo[0].baz[0]=15&foo[0].bar=2', { allowDots: true }), { foo: [{ baz: ['15'], bar: '2' }] }); st.deepEqual(qs.parse('foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2', { allowDots: true }), { foo: [{ baz: ['15', '16'], bar: '2' }] }); - st.deepEqual(qs.parse('foo.bad=baz&foo[0]=bar', { allowDots: true }), { foo: { bad: 'baz', '0': 'bar' } }); - st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar', { allowDots: true }), { foo: { bad: 'baz', '0': 'bar' } }); - st.deepEqual(qs.parse('foo[]=bar&foo.bad=baz', { allowDots: true }), { foo: { '0': 'bar', bad: 'baz' } }); - st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo', { allowDots: true }), { foo: { bad: 'baz', '0': 'bar', '1': 'foo' } }); + st.deepEqual(qs.parse('foo.bad=baz&foo[0]=bar', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar' } }); + st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar' } }); + st.deepEqual(qs.parse('foo[]=bar&foo.bad=baz', { allowDots: true }), { foo: { 0: 'bar', bad: 'baz' } }); + st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar', 1: 'foo' } }); st.deepEqual(qs.parse('foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb', { allowDots: true }), { foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] }); st.end(); }); t.test('correctly prunes undefined values when converting an array to an object', function (st) { - st.deepEqual(qs.parse('a[2]=b&a[99999999]=c'), { a: { '2': 'b', '99999999': 'c' } }); + st.deepEqual(qs.parse('a[2]=b&a[99999999]=c'), { a: { 2: 'b', 99999999: 'c' } }); st.end(); }); @@ -157,7 +157,7 @@ test('parse()', function (t) { }); t.test('doesn\'t produce empty keys', function (st) { - st.deepEqual(qs.parse('_r=1&'), { '_r': '1' }); + st.deepEqual(qs.parse('_r=1&'), { _r: '1' }); st.end(); }); @@ -228,8 +228,8 @@ test('parse()', function (t) { }); t.test('continues parsing when no parent is found', function (st) { - st.deepEqual(qs.parse('[]=&a=b'), { '0': '', a: 'b' }); - st.deepEqual(qs.parse('[]&a=b', { strictNullHandling: true }), { '0': null, a: 'b' }); + st.deepEqual(qs.parse('[]=&a=b'), { 0: '', a: 'b' }); + st.deepEqual(qs.parse('[]&a=b', { strictNullHandling: true }), { 0: null, a: 'b' }); st.deepEqual(qs.parse('[foo]=bar'), { foo: 'bar' }); st.end(); }); @@ -283,9 +283,9 @@ test('parse()', function (t) { }); t.test('allows overriding array limit', function (st) { - st.deepEqual(qs.parse('a[0]=b', { arrayLimit: -1 }), { a: { '0': 'b' } }); + st.deepEqual(qs.parse('a[0]=b', { arrayLimit: -1 }), { a: { 0: 'b' } }); st.deepEqual(qs.parse('a[-1]=b', { arrayLimit: -1 }), { a: { '-1': 'b' } }); - st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayLimit: 0 }), { a: { '0': 'b', '1': 'c' } }); + st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayLimit: 0 }), { a: { 0: 'b', 1: 'c' } }); st.end(); }); @@ -341,13 +341,13 @@ test('parse()', function (t) { t.test('parses an object and not child values', function (st) { var input = { - 'user[name]': { 'pop[bob]': { 'test': 3 } }, + 'user[name]': { 'pop[bob]': { test: 3 } }, 'user[email]': null }; var expected = { user: { - name: { 'pop[bob]': { 'test': 3 } }, + name: { 'pop[bob]': { test: 3 } }, email: null } }; @@ -469,7 +469,7 @@ test('parse()', function (t) { st.deepEqual( qs.parse('a[b]=c&a=toString', { plainObjects: true }), - { a: { b: 'c', toString: true } }, + { __proto__: null, a: { __proto__: null, b: 'c', toString: true } }, 'can overwrite prototype with plainObjects true' ); @@ -494,13 +494,13 @@ test('parse()', function (t) { t.test('can parse with custom encoding', function (st) { st.deepEqual(qs.parse('%8c%a7=%91%e5%8d%e3%95%7b', { decoder: function (str) { - var reg = /\%([0-9A-F]{2})/ig; + var reg = /%([0-9A-F]{2})/ig; var result = []; var parts; - var last = 0; - while (parts = reg.exec(str)) { + // var last = 0; + while ((parts = reg.exec(str))) { result.push(parseInt(parts[1], 16)); - last = parts.index + parts[0].length; + // last = parts.index + parts[0].length; } return iconv.decode(SaferBuffer.from(result), 'shift_jis').toString(); } @@ -509,10 +509,8 @@ test('parse()', function (t) { }); t.test('throws error with wrong decoder', function (st) { - st.throws(function () { - qs.parse({}, { - decoder: 'string' - }); + st['throws'](function () { + qs.parse({}, { decoder: 'string' }); }, new TypeError('Decoder has to be a function.')); st.end(); }); diff --git a/test/stringify.js b/test/stringify.js index 46be2334..7d77f32d 100644 --- a/test/stringify.js +++ b/test/stringify.js @@ -224,7 +224,7 @@ test('stringify()', function (t) { var calls = 0; var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } }; var filterFunc = function (prefix, value) { - calls++; + calls += 1; if (calls === 1) { st.equal(prefix, '', 'prefix is empty'); st.equal(value, obj);