Skip to content

Commit

Permalink
ACT-362 Brackets Support (#993)
Browse files Browse the repository at this point in the history
* add support for brackets inside js keys in get method

* add double quotes

* explanatory text + new link

* safari support

* remove invalid bracket test since it is now supported

* use class based regex to avoid parseError

* actually convert the regex correctly

* cleanup

* split tests by functionality
  • Loading branch information
dlasky authored Jan 25, 2023
1 parent 5c50d75 commit fd54f66
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 18 deletions.
10 changes: 9 additions & 1 deletion packages/core/src/__tests__/get.iso.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ const obj = {
},
h: null
},
u: undefined
u: undefined,
'[txt] non': true,
'[txt] nest': {
inner: true
}
}

const fixtures: Record<string, unknown> = {
Expand All @@ -27,6 +31,10 @@ const fixtures: Record<string, unknown> = {
u: undefined
}

/** note that this test is basically a duplicate of the get.test.ts file,
* only with the tests that safari can't handle due to its lack of lookbehind (ES2018)
* grouping removed so it passes in webkit */

describe('get', () => {
for (const path of Object.keys(fixtures)) {
test(`"${path}"`, () => {
Expand Down
44 changes: 44 additions & 0 deletions packages/core/src/__tests__/get.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { get } from '../get'

const obj = {
a: {
b: {
c: 42,
d: true,
e: 'hello',
f: [{ g: 'yay' }, { g: 'nay' }]
},
h: null
},
u: undefined,
'[txt] non': true,
'[txt] nest': {
inner: true
}
}

const fixtures: Record<string, unknown> = {
'': obj,
a: obj.a,
'a.b': obj.a.b,
"['a'].b": obj.a.b,
'["a"].b': obj.a.b,
'a.b.c': obj.a.b.c,
'a.b.d': obj.a.b.d,
'a.b.e': obj.a.b.e,
'a.b.f[0]': obj.a.b.f[0],
'a.b.f[0].g': obj.a.b.f[0].g,
'a.h': obj.a.h,
'a.b.x': undefined,
'[txt] non': true,
'[txt] nest.inner': true,
u: undefined
}

describe('get', () => {
for (const path of Object.keys(fixtures)) {
test(`"${path}"`, () => {
expect(get(obj, path)).toEqual(fixtures[path])
})
}
})
25 changes: 22 additions & 3 deletions packages/core/src/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
* Lightweight alternative to lodash.get with similar coverage
* Supports basic path lookup via dot notation `'foo.bar[0].baz'` or an array ['foo', 'bar', '0', 'baz']
*/

let arrayRe: RegExp

try {
arrayRe = new RegExp(`\\[(?="|'|\\d)|\\.|(?<="|'|\\d)]+`, 'g')
} catch (e) {
//safari does not support lookbehind operator so we will default
//to a simpler approach wherein [bar] will not be a valid key
arrayRe = /\[|\.|]+/g
}

export function get<T = unknown>(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
obj: any,
Expand All @@ -13,9 +24,17 @@ export function get<T = unknown>(
// Not defined
if (path === null || path == undefined) return undefined

// Check if path is string or array. Regex : ensure that we do not have '.' and brackets.
// Regex explained: https://regexr.com/58j0k
const pathArray = Array.isArray(path) ? path : (path.match(/([^[.\]])+/g) as string[])
// Check if path is string or array. Regex : splits the path into valid array of path chunks
// we support an extra edge case that lodash does not around brackets
// a['b'] = ['a','b']
// a[b] = ['a[b]'] //brackets without numeric index or a string are considered part of the key
// Regex explained: https://regexr.com/74m2m
const pathArray = Array.isArray(path)
? path
: path
.split(arrayRe)
.filter((f) => f)
.map((s) => s.replace(/'|"/g, ''))

// Find value if exist return otherwise return undefined value
return pathArray.reduce((prevObj, key) => prevObj && prevObj[key], obj)
Expand Down
14 changes: 0 additions & 14 deletions packages/core/src/mapping-kit/__tests__/index.iso.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -520,20 +520,6 @@ describe('@path', () => {
expect(output).toStrictEqual({ neat: 'bar' })
})

test('invalid bracket-spaced nested value', () => {
const output = transform(
{ neat: { '@path': "$.integrations['Actions Amplitude'].session_id" } },
{
integrations: {
'Actions Amplitude': {
session_id: 'bar'
}
}
}
)
expect(output).toStrictEqual({})
})

test('invalid nested value type', () => {
const output = transform({ neat: { '@path': '$.foo.bar.baz' } }, { foo: 'bar' })
expect(output).toStrictEqual({})
Expand Down

0 comments on commit fd54f66

Please sign in to comment.