Skip to content

Commit

Permalink
Add unit test and fix bug
Browse files Browse the repository at this point in the history
  • Loading branch information
hans00 committed Aug 12, 2023
1 parent 4e3b05d commit 5802100
Show file tree
Hide file tree
Showing 5 changed files with 1,079 additions and 24 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
on: push

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npm test
44 changes: 44 additions & 0 deletions __tests__/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import chai from 'chai'
import qs from 'qs'
import parse from '../index.mjs'

describe('parse', () => {
it('array', () => {
chai.expect(parse('a[]&a[]&a[]&a[]&a[]&a[]')).to.be.deep.equal({
a: ['', '', '', '', '', ''],
})
chai.expect(parse('a&a&a&a[]&a[]&a[]')).to.be.deep.equal({
a: ['', '', '', '', '', ''],
})
})

it('nested array', () => {
chai.expect(parse('a[][][]&a[][][]&a[][][]&a[][]&a[][]&a[]')).to.be.deep.equal({
a: [[['', '', '']], ['', ''], ''],
})
})

it('array with index', () => {
chai.expect(parse('a[3]=3&a[5]=5&a[2]=2&a[4]=4&a[1]=1&a[0]=0&a[10]=10')).to.be.deep.equal({
a: ['0', '1', '2', '3', '4', '5', undefined, undefined, undefined, undefined, '10'],
})
})

it('object', () => {
chai.expect(parse('a[a]&a[b]&a[c]')).to.be.deep.equal({
a: { a: '', b: '', c: '' },
})
})

it('nested object', () => {
chai.expect(parse('a[a][a]&a[a][b]&a[a][c]')).to.be.deep.equal({
a: { a: { a: '', b: '', c: '' } },
})
})

it('drop insecure key', () => {
chai.expect(parse('a[constructor][prototype][a]=1')).to.be.deep.equal({
a: undefined,
})
})
})
49 changes: 30 additions & 19 deletions index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,68 @@ import { parse as parser } from 'fast-querystring'

const isPosInt = (str) => {
const n = Number(str)
return n !== Infinity && Number.isInteger(n) && n >= 0
return Number.isInteger(n) && n >= 0
}

const resolvePath = (o, path, v=null, d) => {
if (!path) return o ? [].concat(o, v) : v
if (!path) {
if (!o) return v
o.push(v)
return o
}
if (d === 0) return { [path]: v }
const l = path.indexOf('[')
const r = path.indexOf(']')
if (l === -1 || r === -1 || l > r) return { [path]: v }
const k = path.slice(l + 1, r) || (o?.length ?? '0')
const k = path.slice(l + 1, r) || o?.length || 0
const next = path.slice(r + 1)
if (isPosInt(k)) {
let i = Number(k)
if (o && typeof o === 'object' && !Array.isArray(o)) {
if (!i) i = Object.keys(o).length
if (!o) o = []
else if (!Array.isArray(o)) {
if (!k && !i) i = Object.keys(o).length
return {
...o,
[i]: resolvePath(o[i], next, v, d - 1, opt),
[i]: resolvePath(o[i], next, v, d - 1),
}
}
if (i > 0) {
if (!o) o = []
for (let len = o.length; len <= i; len += 1) {
o.push()
}
} else if (!o) {
o = []
const extend = i - o.length + 1
if (extend > 0) {
o.push.apply(o, Array.apply(null, Array(extend)))
}
if (next === '[]' && typeof v !== 'string' && (!o[i] || Array.isArray(o[i]))) {
o[i] ??= []
o[i].push.apply(o[i], v)
} else {
o[i] = resolvePath(o[i], next, v, d - 1)
}
o[i] = resolvePath(o[i], next, v, d - 1)
return o
} else if (!{}[k]) {
return {
...o,
[k]: resolvePath(o?.[k], next, v, d - 1),
}
} else {
return null
return o
}
}

export default (str, depth=5) =>
Object.entries(parser(str))
.reduce((o, [k, v]) => {
const l = k.indexOf('[')
const r = k.indexOf(']')
if (l > 0 && r > l) {
if (l > 0 && k.indexOf(']') > l) {
const key = k.slice(0, l)
const path = k.slice(l)
if (path === '[]') {
if (o[key]) o[key] = [].concat(o[key], v)
else o[key] = v
if (key in o) {
if (!Array.isArray(o[key]))
o[key] = [o[key]]
o[key].push.apply(
o[key],
typeof v === 'string' ? [v] : v
)
} else o[key] = v
} else {
o[key] = resolvePath(o[key], path, v, depth)
}
Expand Down
Loading

0 comments on commit 5802100

Please sign in to comment.