diff --git a/src/emitters/streamed-emitter.ts b/src/emitters/streamed-emitter.ts index 17b7690f5..da5aa28f5 100644 --- a/src/emitters/streamed-emitter.ts +++ b/src/emitters/streamed-emitter.ts @@ -1,11 +1,15 @@ import { stringify } from '../util/underscore' +import { Emitter } from './emitter' -export class StreamedEmitter { +export class StreamedEmitter implements Emitter { public buffer = ''; - public stream = new (require('stream').PassThrough)() + public stream: NodeJS.ReadWriteStream = new (require('stream').PassThrough)() public write (html: any) { this.stream.write(stringify(html)) } + public error (err: Error) { + this.stream.emit('error', err) + } public end () { this.stream.end() } diff --git a/src/liquid.ts b/src/liquid.ts index be1ec692f..e9c292910 100644 --- a/src/liquid.ts +++ b/src/liquid.ts @@ -84,6 +84,10 @@ export class Liquid { const templates = this.parseFileSync(file) return this.renderSync(templates, ctx) } + public async renderFileToNodeStream (file: string, scope?: object) { + const templates = await this.parseFile(file) + return this.renderToNodeStream(templates, scope) + } public _evalValue (str: string, ctx: Context): IterableIterator { const value = new Value(str, this) diff --git a/src/render/render.ts b/src/render/render.ts index 4d7eca6aa..97ac60b92 100644 --- a/src/render/render.ts +++ b/src/render/render.ts @@ -10,7 +10,8 @@ import { KeepingTypeEmitter } from '../emitters/keeping-type-emitter' export class Render { public renderTemplatesToNodeStream (templates: Template[], ctx: Context): NodeJS.ReadableStream { const emitter = new StreamedEmitter() - toThenable(this.renderTemplates(templates, ctx, emitter)).then(() => emitter.end()) + toThenable(this.renderTemplates(templates, ctx, emitter)) + .then(() => emitter.end(), err => emitter.error(err)) return emitter.stream } public * renderTemplates (templates: Template[], ctx: Context, emitter?: Emitter): IterableIterator { diff --git a/test/.eslintrc.json b/test/.eslintrc.json index 14822e762..870214ca1 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -6,6 +6,7 @@ "mocha" ], "rules": { + "@typescript-eslint/no-var-requires": "off", "no-unused-expressions": "off", "no-new": "off" } diff --git a/test/e2e/render-to-node-stream.ts b/test/e2e/render-to-node-stream.ts new file mode 100644 index 000000000..c16626477 --- /dev/null +++ b/test/e2e/render-to-node-stream.ts @@ -0,0 +1,56 @@ +import { expect, use } from 'chai' +import { resolve } from 'path' +import * as chaiAsPromised from 'chai-as-promised' + +use(chaiAsPromised) + +describe('.renderToNodeStream()', function () { + it('should render to stream in Node.js', done => { + const cjs = require('../../dist/liquid.node.cjs') + const engine = new cjs.Liquid() + const tpl = engine.parseFileSync(resolve(__dirname, '../stub/root/foo.html')) + const stream = engine.renderToNodeStream(tpl) + let html = '' + stream.on('data', (data: string) => { html += data }) + stream.on('end', () => { + try { + expect(html).to.equal('foo') + done() + } catch (err) { + done(err) + } + }) + }) + it('should throw in browser', async function () { + const cjs = require('../../dist/liquid.browser.umd') + const engine = new cjs.Liquid() + const render = () => engine.renderToNodeStream('foo') + return expect(render).to.throw('streaming not supported in browser') + }) +}) + +describe('.renderFileToNodeStream()', function () { + it('should render to stream in Node.js', async done => { + const cjs = require('../../dist/liquid.node.cjs') + const engine = new cjs.Liquid({ + root: resolve(__dirname, '../stub/root/') + }) + const stream = await engine.renderFileToNodeStream('foo.html') + let html = '' + stream.on('data', (data: string) => { html += data }) + stream.on('end', () => { + try { + expect(html).to.equal('foo') + done() + } catch (err) { + done(err) + } + }) + }) + it('should throw in browser', async function () { + const cjs = require('../../dist/liquid.browser.umd') + const engine = new cjs.Liquid() + const render = () => engine.renderFileToNodeStream('foo') + return expect(render).to.throw('streaming not supported in browser') + }) +}) diff --git a/test/integration/liquid/liquid.ts b/test/integration/liquid/liquid.ts index 55bf67ded..eb25dcfc7 100644 --- a/test/integration/liquid/liquid.ts +++ b/test/integration/liquid/liquid.ts @@ -136,7 +136,36 @@ describe('Liquid', function () { const stream = engine.renderToNodeStream(engine.parse('{{"foo"}}')) let html = '' stream.on('data', data => { html += data }) - stream.on('end', () => { expect(html).to.equal('foo'); done() }) + stream.on('end', () => { + try { + expect(html).to.equal('foo') + done() + } catch (err) { + done(err) + } + }) + }) + }) + describe('#enderFileToNodeStream', function () { + before(function () { + mock({ + '/root/foo.html': 'foo' + }) + }) + after(restore) + it('should render a simple value', (done) => { + const engine = new Liquid({ root: ['/root/'] }) + engine.renderFileToNodeStream('foo.html').then(stream => { + let html = '' + stream.on('data', data => { html += data }) + stream.on('end', () => { + try { + expect(html).to.equal('foo'); done() + } catch (err) { + done(err) + } + }) + }) }) }) }) diff --git a/test/unit/emitters/streamed-emitter-browser.ts b/test/unit/emitters/streamed-emitter-browser.ts new file mode 100644 index 000000000..5669a7d8e --- /dev/null +++ b/test/unit/emitters/streamed-emitter-browser.ts @@ -0,0 +1,8 @@ +import { expect } from 'chai' +import { StreamedEmitter } from '../../../src/emitters/streamed-emitter-browser' + +describe('emitters/streamed-emitter-browser', () => { + it('should throw when try to constructing', () => { + expect(() => new StreamedEmitter()).to.throw(/streaming not supported/) + }) +})