From 1167b235e6356e7854ee4141f85224612742a3cf Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 1 Jun 2022 10:42:20 -0400 Subject: [PATCH 1/5] implemented using eval --- packages/common/src/constants/regexp.ts | 3 ++- packages/common/src/utils/variables.ts | 14 ++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/common/src/constants/regexp.ts b/packages/common/src/constants/regexp.ts index 95d19d708..3917128ac 100644 --- a/packages/common/src/constants/regexp.ts +++ b/packages/common/src/constants/regexp.ts @@ -6,7 +6,8 @@ export const SLOT_ANNOTATION_SIMPLE_REGEX = /{([^ .[\]{}]+?)}/g; export const IS_VARIABLE_REGEXP = /^{.*}$/; -export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})}/g; +// export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})}/g; +export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})((?:\.\w{1,64}|\[\d\])*)}/i; export const VALID_CHARACTER = 'a-zA-Z'; diff --git a/packages/common/src/utils/variables.ts b/packages/common/src/utils/variables.ts index 58d737b5e..0232eee03 100644 --- a/packages/common/src/utils/variables.ts +++ b/packages/common/src/utils/variables.ts @@ -3,14 +3,18 @@ import { READABLE_VARIABLE_REGEXP } from '@common/constants'; export const variableReplacer = ( match: string, inner: string, + selectors: string[], variables: Record, modifier?: (variable: unknown) => unknown ): unknown => { - if (inner in variables) { - return typeof modifier === 'function' ? modifier(variables[inner]) : variables[inner]; + if (!(inner in variables)) { + return match; } - return match; + // TODO this is not very nice + const replaced = eval(`variables[inner]${selectors[0]}`); + + return typeof modifier === 'function' ? modifier(replaced) : replaced; }; export const replaceVariables = ( @@ -23,7 +27,9 @@ export const replaceVariables = ( return ''; } - return phrase.replace(READABLE_VARIABLE_REGEXP, (match, inner) => String(variableReplacer(match, inner, variables, modifier))); + return phrase.replace(READABLE_VARIABLE_REGEXP, (match, [inner, ...selectors]) => + String(variableReplacer(match, inner, selectors, variables, modifier)) + ); }; // turn float variables to 2 decimal places From 96556996682d4e1199b7cd137a84fed936047b77 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 1 Jun 2022 12:36:33 -0400 Subject: [PATCH 2/5] alternate method of evaluating selectors --- packages/common/src/constants/regexp.ts | 2 +- packages/common/src/utils/variables.ts | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/common/src/constants/regexp.ts b/packages/common/src/constants/regexp.ts index 3917128ac..e89a0eb10 100644 --- a/packages/common/src/constants/regexp.ts +++ b/packages/common/src/constants/regexp.ts @@ -7,7 +7,7 @@ export const SLOT_ANNOTATION_SIMPLE_REGEX = /{([^ .[\]{}]+?)}/g; export const IS_VARIABLE_REGEXP = /^{.*}$/; // export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})}/g; -export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})((?:\.\w{1,64}|\[\d\])*)}/i; +export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})((?:\.\w{1,64}|\[\d+\])*)}/i; export const VALID_CHARACTER = 'a-zA-Z'; diff --git a/packages/common/src/utils/variables.ts b/packages/common/src/utils/variables.ts index 0232eee03..63ad21cab 100644 --- a/packages/common/src/utils/variables.ts +++ b/packages/common/src/utils/variables.ts @@ -11,8 +11,25 @@ export const variableReplacer = ( return match; } - // TODO this is not very nice - const replaced = eval(`variables[inner]${selectors[0]}`); + let replaced: any = variables[inner]; + + let selectorString = selectors[0]; + while (selectorString.length > 0) { + selectorString = selectorString.replace(/^\.(\w{1,64})/, (_m, field) => { + replaced = replaced[field]; + return ''; + }); + if (replaced === undefined) { + break; + } + selectorString = selectorString.replace(/^\[(\d+)\]/, (_m, index) => { + replaced = replaced[index]; + return ''; + }); + if (replaced === undefined) { + break; + } + } return typeof modifier === 'function' ? modifier(replaced) : replaced; }; From 62d20f65064b2e1661e09f81704f5843b06ed18b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 1 Jun 2022 14:37:08 -0400 Subject: [PATCH 3/5] add tests --- packages/common/src/constants/regexp.ts | 2 +- packages/common/src/utils/variables.ts | 2 +- packages/common/tests/utils/variables.unit.ts | 59 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 packages/common/tests/utils/variables.unit.ts diff --git a/packages/common/src/constants/regexp.ts b/packages/common/src/constants/regexp.ts index e89a0eb10..7c5da55d2 100644 --- a/packages/common/src/constants/regexp.ts +++ b/packages/common/src/constants/regexp.ts @@ -7,7 +7,7 @@ export const SLOT_ANNOTATION_SIMPLE_REGEX = /{([^ .[\]{}]+?)}/g; export const IS_VARIABLE_REGEXP = /^{.*}$/; // export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})}/g; -export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})((?:\.\w{1,64}|\[\d+\])*)}/i; +export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})((?:\.\w{1,64}|\[\d+\])*)}/ig; export const VALID_CHARACTER = 'a-zA-Z'; diff --git a/packages/common/src/utils/variables.ts b/packages/common/src/utils/variables.ts index 63ad21cab..68e5547ee 100644 --- a/packages/common/src/utils/variables.ts +++ b/packages/common/src/utils/variables.ts @@ -44,7 +44,7 @@ export const replaceVariables = ( return ''; } - return phrase.replace(READABLE_VARIABLE_REGEXP, (match, [inner, ...selectors]) => + return phrase.replace(READABLE_VARIABLE_REGEXP, (match, inner, ...selectors) => String(variableReplacer(match, inner, selectors, variables, modifier)) ); }; diff --git a/packages/common/tests/utils/variables.unit.ts b/packages/common/tests/utils/variables.unit.ts new file mode 100644 index 000000000..1d9f5c7b5 --- /dev/null +++ b/packages/common/tests/utils/variables.unit.ts @@ -0,0 +1,59 @@ + +import { replaceVariables } from '@common/utils/variables'; + +import { expect } from 'chai'; + +describe('Utils | variables', () => { + describe('replaceVariables', () => { + const variables = { + 'name': 'bob', + 'age': 32, + 'favFoods': [ + 'takoyaki', 'onigiri', 'taiyaki' + ], + 'job': { + 'company': 'voiceflow', + 'position': 'software engineer', + 'team': 'creator' + } + }; + it('correctly replaces simple variables', () => { + expect(replaceVariables('hello, my name is {name}, and i am {age} years old', variables)) + .to.eq('hello, my name is bob, and i am 32 years old'); + expect(replaceVariables('{name} {name} {name}', variables)) + .to.eq('bob bob bob'); + }); + it('variables that are not defined do not get expanded', () => { + expect(replaceVariables('hello, my name is {name} and i work at {workplace}', variables)) + .to.eq('hello, my name is bob and i work at {workplace}'); + expect(replaceVariables('hello, my name is {Name}', variables)) + .to.eq('hello, my name is {Name}'); + }); + + it('array access works', () => { + expect(replaceVariables('most favorite food is {favFoods[0]}, second favorite is {favFoods[1]}, and third is {favFoods[2]}', variables)) + .to.eq('most favorite food is takoyaki, second favorite is onigiri, and third is taiyaki'); + }); + it('index out of range', () => { + }); + + it('object access works', () => { + expect(replaceVariables('i work at {job.company} as a {job.position} on the {job.team} team', variables)) + .to.eq('i work at voiceflow as a software engineer on the creator team'); + }); + it('non-existent fields', () => { + }); + + it('weird cases', () => { + const variables = { + '': 6969, + 'var': '{name}', + } + expect(replaceVariables('this is a blank variable {}', variables)) + .to.eq('this is a blank variable {}'); + expect(replaceVariables('{var}', variables)) + .to.eq('{name}'); + }); + + }); +}); From 5dcbdc9c6c93814f1b4781a8af401f703f26f61e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 1 Jun 2022 14:44:59 -0400 Subject: [PATCH 4/5] fix lint --- packages/common/src/constants/regexp.ts | 2 +- packages/common/src/utils/variables.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/common/src/constants/regexp.ts b/packages/common/src/constants/regexp.ts index 7c5da55d2..02c9375c8 100644 --- a/packages/common/src/constants/regexp.ts +++ b/packages/common/src/constants/regexp.ts @@ -7,7 +7,7 @@ export const SLOT_ANNOTATION_SIMPLE_REGEX = /{([^ .[\]{}]+?)}/g; export const IS_VARIABLE_REGEXP = /^{.*}$/; // export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})}/g; -export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})((?:\.\w{1,64}|\[\d+\])*)}/ig; +export const READABLE_VARIABLE_REGEXP = /{(\w{1,64})((?:\.\w{1,64}|\[\d+])*)}/gi; export const VALID_CHARACTER = 'a-zA-Z'; diff --git a/packages/common/src/utils/variables.ts b/packages/common/src/utils/variables.ts index 68e5547ee..829c10ae6 100644 --- a/packages/common/src/utils/variables.ts +++ b/packages/common/src/utils/variables.ts @@ -15,6 +15,7 @@ export const variableReplacer = ( let selectorString = selectors[0]; while (selectorString.length > 0) { + // eslint-disable-next-line no-loop-func selectorString = selectorString.replace(/^\.(\w{1,64})/, (_m, field) => { replaced = replaced[field]; return ''; @@ -22,7 +23,8 @@ export const variableReplacer = ( if (replaced === undefined) { break; } - selectorString = selectorString.replace(/^\[(\d+)\]/, (_m, index) => { + // eslint-disable-next-line no-loop-func + selectorString = selectorString.replace(/^\[(\d+)]/, (_m, index) => { replaced = replaced[index]; return ''; }); From 9189edb5a0ffb2f0cc7b314b2524650134d4d7f4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 1 Jun 2022 14:53:17 -0400 Subject: [PATCH 5/5] format fix :P --- packages/common/tests/utils/variables.unit.ts | 61 ++++++++----------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/packages/common/tests/utils/variables.unit.ts b/packages/common/tests/utils/variables.unit.ts index 1d9f5c7b5..d23123b70 100644 --- a/packages/common/tests/utils/variables.unit.ts +++ b/packages/common/tests/utils/variables.unit.ts @@ -1,59 +1,50 @@ - import { replaceVariables } from '@common/utils/variables'; - import { expect } from 'chai'; describe('Utils | variables', () => { describe('replaceVariables', () => { const variables = { - 'name': 'bob', - 'age': 32, - 'favFoods': [ - 'takoyaki', 'onigiri', 'taiyaki' - ], - 'job': { - 'company': 'voiceflow', - 'position': 'software engineer', - 'team': 'creator' - } + name: 'bob', + age: 32, + favFoods: ['takoyaki', 'onigiri', 'taiyaki'], + job: { + company: 'voiceflow', + position: 'software engineer', + team: 'creator', + }, }; it('correctly replaces simple variables', () => { - expect(replaceVariables('hello, my name is {name}, and i am {age} years old', variables)) - .to.eq('hello, my name is bob, and i am 32 years old'); - expect(replaceVariables('{name} {name} {name}', variables)) - .to.eq('bob bob bob'); + expect(replaceVariables('hello, my name is {name}, and i am {age} years old', variables)).to.eq('hello, my name is bob, and i am 32 years old'); + expect(replaceVariables('{name} {name} {name}', variables)).to.eq('bob bob bob'); }); it('variables that are not defined do not get expanded', () => { - expect(replaceVariables('hello, my name is {name} and i work at {workplace}', variables)) - .to.eq('hello, my name is bob and i work at {workplace}'); - expect(replaceVariables('hello, my name is {Name}', variables)) - .to.eq('hello, my name is {Name}'); + expect(replaceVariables('hello, my name is {name} and i work at {workplace}', variables)).to.eq( + 'hello, my name is bob and i work at {workplace}' + ); + expect(replaceVariables('hello, my name is {Name}', variables)).to.eq('hello, my name is {Name}'); }); it('array access works', () => { - expect(replaceVariables('most favorite food is {favFoods[0]}, second favorite is {favFoods[1]}, and third is {favFoods[2]}', variables)) - .to.eq('most favorite food is takoyaki, second favorite is onigiri, and third is taiyaki'); - }); - it('index out of range', () => { + expect(replaceVariables('most favorite food is {favFoods[0]}, second favorite is {favFoods[1]}, and third is {favFoods[2]}', variables)).to.eq( + 'most favorite food is takoyaki, second favorite is onigiri, and third is taiyaki' + ); }); + // it('index out of range', () => {}); it('object access works', () => { - expect(replaceVariables('i work at {job.company} as a {job.position} on the {job.team} team', variables)) - .to.eq('i work at voiceflow as a software engineer on the creator team'); - }); - it('non-existent fields', () => { + expect(replaceVariables('i work at {job.company} as a {job.position} on the {job.team} team', variables)).to.eq( + 'i work at voiceflow as a software engineer on the creator team' + ); }); + // it('non-existent fields', () => {}); it('weird cases', () => { const variables = { '': 6969, - 'var': '{name}', - } - expect(replaceVariables('this is a blank variable {}', variables)) - .to.eq('this is a blank variable {}'); - expect(replaceVariables('{var}', variables)) - .to.eq('{name}'); + var: '{name}', + }; + expect(replaceVariables('this is a blank variable {}', variables)).to.eq('this is a blank variable {}'); + expect(replaceVariables('{var}', variables)).to.eq('{name}'); }); - }); });