diff --git a/lib/template.js b/lib/template.js index cf4016e..6c62c73 100644 --- a/lib/template.js +++ b/lib/template.js @@ -93,6 +93,19 @@ const renderRecursively = (inputJson, template, execOptions = {}) => { if (typeof template === 'object' && template !== null) { return Object.fromEntries( Object.entries(template).flatMap(([key, value]) => { + const SPREAD_KEYWORD = "spreadValue"; + const keywordMatcher = `^\\{\\{\\s*${SPREAD_KEYWORD}\\(\\s*\\)\\s*\\}\\}$`; // matches {{ () }} with white spaces where you'd expect them + + if (key.trim().match(keywordMatcher)) { + const evaluatedValue = renderRecursively(inputJson, value, execOptions); + if (typeof evaluatedValue !== "object") { + throw new Error( + `Evaluated value should be an object if the key is ${key}. Original value: ${value}, evaluated to: ${JSON.stringify(evaluatedValue)}` + ); + } + return Object.entries(evaluatedValue); + } + const evaluatedKey = renderRecursively(inputJson, key, execOptions); if (!['undefined', 'string'].includes(typeof evaluatedKey) && evaluatedKey !== null) { throw new Error( diff --git a/package-lock.json b/package-lock.json index b590a7c..773bfb5 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@port-labs/jq-node-bindings", - "version": "v0.0.12", + "version": "v0.0.13", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@port-labs/jq-node-bindings", - "version": "v0.0.12", + "version": "v0.0.13", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index cd941e5..7feb013 100755 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "@port-labs/jq-node-bindings", - "version": "v0.0.12", + "version": "v0.0.13", "description": "Node.js bindings for JQ", - "jq-node-bindings": "0.0.12", + "jq-node-bindings": "0.0.13", "main": "lib/index.js", "scripts": { "configure": "node-gyp configure", diff --git a/test/template.test.js b/test/template.test.js index 69c7fee..6478a0d 100644 --- a/test/template.test.js +++ b/test/template.test.js @@ -125,6 +125,14 @@ describe('template', () => { expect(render({'{{""}}': 'bar'})).toEqual({}); expect(render({'{{\'\'}}': 'bar'})).toEqual({}); }); + it('testing spread key', () => { + const json = { foo: "bar" }; + const render = (input) => jq.renderRecursively(json, input); + + expect(render({ "{{spreadValue()}}": { foo: "bar" } })).toEqual({foo: "bar"}); + expect(render({ " {{ spreadValue( ) }} ": { foo: "bar" } })).toEqual({foo: "bar"}); + expect(render({ "{{spreadValue()}}": "{{ . }}" })).toEqual({ foo: "bar" }); + }); it('recursive templates should work', () => { const json = { foo: 'bar', bar: 'foo' }; const render = (input) => jq.renderRecursively(json, input); @@ -165,6 +173,10 @@ describe('template', () => { expect(() => { jq.renderRecursively({foo: "bar"}, '{{.foo + 1}}', {throwOnError: true}) }).toThrow("jq: error: string (\"bar\") and number (1) cannot be added"); expect(() => { jq.renderRecursively({}, '{{foo}}/{{bar}}', {throwOnError: true}) }).toThrow("jq: compile error: foo/0 is not defined at , line 1:"); expect(() => { jq.renderRecursively({}, '/{{foo}}/', {throwOnError: true}) }).toThrow("jq: compile error: foo/0 is not defined at , line 1:"); + expect(() => { jq.renderRecursively({}, { "{{ spreadValue() }}": "str" }, { throwOnError: true }) }) + .toThrow('Evaluated value should be an object if the key is {{ spreadValue() }}. Original value: str, evaluated to: "str"'); + expect(() => { jq.renderRecursively({}, { "{{ spreadValue() }}": "{{ \"str\" }}" }, { throwOnError: true }) }) + .toThrow('Evaluated value should be an object if the key is {{ spreadValue() }}. Original value: {{ \"str\" }}, evaluated to: "str"'); }) })