diff --git a/src/render/emitter.ts b/src/render/emitter.ts index 40b6f2c19..676fbc50f 100644 --- a/src/render/emitter.ts +++ b/src/render/emitter.ts @@ -9,7 +9,11 @@ export class Emitter { } public write (html: any) { - if (this.keepOutputType && typeof html !== 'string') { + // This will only preserve the type if the value is isolated. + // I.E: + // {{ my-port }} -> 42 + // {{ my-host }}:{{ my-port }} -> 'host:42' + if (this.keepOutputType === true && typeof html !== 'string' && this.html === '') { this.html = html } else { this.html += html as string diff --git a/test/integration/liquid/keepoutput-opt.ts b/test/integration/liquid/keepoutput-opt.ts new file mode 100644 index 000000000..7df5148d5 --- /dev/null +++ b/test/integration/liquid/keepoutput-opt.ts @@ -0,0 +1,40 @@ +import { expect } from 'chai' +import { Liquid } from '../../../src/liquid' + +describe('LiquidOptions#*keepOutputType*', function () { + it('should respect keepOutputType', async function () { + const engine = new Liquid({ + keepOutputType: true + }) + const context = { + 'my-boolean': true, + 'my-number': 42, + 'my-string': 'test' + } + const booleanHtml = await engine.parseAndRender('{{my-boolean}}', context) + expect(booleanHtml).to.equal(true) + const numberHtml = await engine.parseAndRender('{{my-number}}', context) + expect(numberHtml).to.equal(42) + const html = await engine.parseAndRender('{{my-string}}', context) + expect(html).to.equal('test') + const composedHtml = await engine.parseAndRender('{{my-string}}:{{my-number}}', context) + expect(composedHtml).to.equal('test:42') + }) + + it('should respect keepOutputType = false as default', async function () { + const engine = new Liquid() + const context = { + 'my-boolean': true, + 'my-number': 42, + 'my-string': 'test' + } + const booleanHtml = await engine.parseAndRender('{{my-boolean}}', context) + expect(booleanHtml).to.equal('true') + const numberHtml = await engine.parseAndRender('{{my-number}}', context) + expect(numberHtml).to.equal('42') + const html = await engine.parseAndRender('{{my-string}}', context) + expect(html).to.equal('test') + const composedHtml = await engine.parseAndRender('{{my-string}}:{{my-number}}', context) + expect(composedHtml).to.equal('test:42') + }) +}) diff --git a/test/unit/template/output.ts b/test/unit/template/output.ts index e19f2d8c5..52aebaaec 100644 --- a/test/unit/template/output.ts +++ b/test/unit/template/output.ts @@ -4,6 +4,7 @@ import { Context } from '../../../src/context/context' import { Output } from '../../../src/template/output' import { OutputToken } from '../../../src/tokens/output-token' import { FilterMap } from '../../../src/template/filter/filter-map' +import { defaultOptions } from '../../../src/liquid-options' const expect = chai.expect @@ -42,4 +43,55 @@ describe('Output', function () { await toThenable(output.render(scope, emitter)) return expect(emitter.html).to.equal('FOO') }) + context('when keepOutputType is enabled', () => { + const emitter: any = { + write: (html: any) => { + if (emitter.keepOutputType && typeof html !== 'string') { + emitter.html = html + } else { + emitter.html += html as string + } + }, + html: '', + keepOutputType: true + } + + beforeEach(function () { + filters = new FilterMap(false, liquid) + emitter.html = '' + }) + + it('should respect output variable number type', async () => { + const scope = new Context({ + foo: 42 + }, { ...defaultOptions, keepOutputType: true }) + const output = new Output({ content: 'foo' } as OutputToken, filters, liquid) + await toThenable(output.render(scope, emitter)) + return expect(emitter.html).to.equal(42) + }) + it('should respect output variable boolean type', async () => { + const scope = new Context({ + foo: true + }, { ...defaultOptions, keepOutputType: true }) + const output = new Output({ content: 'foo' } as OutputToken, filters, liquid) + await toThenable(output.render(scope, emitter)) + return expect(emitter.html).to.equal(true) + }) + it('should respect output variable object type', async () => { + const scope = new Context({ + foo: 'test' + }, { ...defaultOptions, keepOutputType: true }) + const output = new Output({ content: 'foo' } as OutputToken, filters, liquid) + await toThenable(output.render(scope, emitter)) + return expect(emitter.html).to.equal('test') + }) + it('should respect output variable string type', async () => { + const scope = new Context({ + foo: { a: { b: 42 } } + }, { ...defaultOptions, keepOutputType: true }) + const output = new Output({ content: 'foo' } as OutputToken, filters, liquid) + await toThenable(output.render(scope, emitter)) + return expect(emitter.html).to.deep.equal({ a: { b: 42 } }) + }) + }) })