diff --git a/benchmark/.eslintrc.json b/benchmark/.eslintrc.json new file mode 100644 index 000000000..b93af0c5e --- /dev/null +++ b/benchmark/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "rules": { + "no-unused-expressions": "off" + } +} diff --git a/benchmark/demo.ts b/benchmark/demo.ts new file mode 100644 index 000000000..98b10994a --- /dev/null +++ b/benchmark/demo.ts @@ -0,0 +1,49 @@ +import * as Benchmark from 'benchmark' +import Liquid from 'src/liquid' +import TagToken from 'src/parser/tag-token' +import Scope from 'src/scope/scope' + +const engine = new Liquid({ + root: __dirname, + extname: '.liquid' +}) + +engine.registerTag('header', { + parse: function (token: TagToken) { + const [key, val] = token.args.split(':') + this[key] = val + }, + render: function (scope: Scope) { + const title = this.liquid.evalValue(this.content, scope) + return `

${title}

` + } +}) + +const ctx = { + todos: ['fork and clone', 'make it better', 'make a pull request'], + title: 'Welcome to liquidjs!' +} + +const template = ` +{%header content: "welcome to liquid" | capitalize%} + + +` + +export default function () { + console.log('--- demo ---') + return new Promise(resolve => { + new Benchmark.Suite('demo') + .add('demo', { + defer: true, + fn: (d: any) => engine.parseAndRender(template, ctx).then(x => d.resolve(x)) + }) + .on('cycle', (event: any) => console.log(String(event.target))) + .on('complete', resolve) + .run({ 'async': true }) + }) +} diff --git a/benchmark/index.ts b/benchmark/index.ts new file mode 100644 index 000000000..c61ad558d --- /dev/null +++ b/benchmark/index.ts @@ -0,0 +1,11 @@ +import output from './output' +import tag from './tag' +import demo from './demo' + +async function main () { + await output() + await tag() + await demo() +} + +main() diff --git a/benchmark/output.ts b/benchmark/output.ts new file mode 100644 index 000000000..50a7f4cc3 --- /dev/null +++ b/benchmark/output.ts @@ -0,0 +1,26 @@ +import * as Benchmark from 'benchmark' +import Liquid from '../src/liquid' + +const liquid = new Liquid() + +export default function () { + console.log('--- output ---') + return new Promise(resolve => { + new Benchmark.Suite('output') + .add('literal', test('{{false}}{{"foo"}}{{32.322}}')) + .add('truncate', test('{{"foobar" | truncate: 3}}')) + .add('date', test('{{"now" | date: "%d%Y%m"}}')) + .add('escape', test('{{"1<2" | escape}}')) + .add('default', test('{{"" | default: 3}}')) + .on('cycle', (event: any) => console.log(String(event.target))) + .on('complete', resolve) + .run({ 'async': true }) + }) +} + +function test (str: string) { + return { + defer: true, + fn: (d: any) => liquid.parseAndRender(str).then(x => d.resolve(x)) + } +} diff --git a/benchmark/tag.ts b/benchmark/tag.ts new file mode 100644 index 000000000..292dc3591 --- /dev/null +++ b/benchmark/tag.ts @@ -0,0 +1,30 @@ +import * as Benchmark from 'benchmark' +import Liquid from '../src/liquid' + +const liquid = new Liquid() + +export default function () { + console.log('--- tag ---') + return new Promise(resolve => { + new Benchmark.Suite('tag') + .add('if', test('{% if "foobar" %}foo{% endif %}')) + .add('unless', test('{%unless "foo"%}true{%else%}false{%endunless%}')) + .add('for', test('{% for i in (1..3) %}{{fooloop.index}}{% endfor %}')) + .add('switch', test('{%case 3%}{% when 1 %}1{% when 2 %}2{% when 3 %}3{%endcase%}')) + .add('assign', test('{%assign a="foo bar"%}')) + .add('capture', test('{%capture foo%}what is this{%endcapture%}')) + .add('increment', test('{%increment a%}')) + .add('decrement', test('{%decrement a%}')) + .add('tablerow', test('{%tablerow i in (1..10) cols:3%}{%endtablerow%}')) + .on('cycle', (event: any) => console.log(String(event.target))) + .on('complete', resolve) + .run({ 'async': true }) + }) +} + +function test (str: string) { + return { + defer: true, + fn: (d: any) => liquid.parseAndRender(str).then(x => d.resolve(x)) + } +} diff --git a/package-lock.json b/package-lock.json index 42482b208..9420c5206 100644 --- a/package-lock.json +++ b/package-lock.json @@ -329,6 +329,12 @@ "lodash": "^4.17.11" } }, + "@types/benchmark": { + "version": "1.0.31", + "resolved": "https://registry.npmjs.org/@types/benchmark/-/benchmark-1.0.31.tgz", + "integrity": "sha512-F6fVNOkGEkSdo/19yWYOwVKGvzbTeWkR/XQYBKtGBQ9oGRjBN9f/L4aJI4sDcVPJO58Y1CJZN8va9V2BhrZapA==", + "dev": true + }, "@types/body-parser": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", @@ -884,6 +890,16 @@ "integrity": "sha512-zyPgY5dgbf99c0uGUjhY4w+mxqEGxPKg9RQDl34VvrVh2bM31lFN+mwR1ZHepq/KA3VCPk1gwJZL6IIJqjLy2w==", "dev": true }, + "benchmark": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", + "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", + "dev": true, + "requires": { + "lodash": "^4.17.4", + "platform": "^1.3.3" + } + }, "body-parser": { "version": "1.18.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", @@ -8734,6 +8750,12 @@ "find-up": "^2.1.0" } }, + "platform": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz", + "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==", + "dev": true + }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", diff --git a/package.json b/package.json index 4314bd377..b5e07da2c 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "unit": "mocha 'test/unit/**/*.ts'", "e2e": "npm run build && mocha 'test/e2e/**/*.ts'", "test": "npm run unit && npm run e2e", + "benchmark": "ts-node --require tsconfig-paths/register benchmark", "coverage-html": "nyc --reporter=html npm run unit", "coverage-coveralls": "nyc npm run unit && nyc report --reporter=text-lcov | coveralls", "build": "rollup -c rollup.config.ts && ls -lh dist", @@ -46,6 +47,7 @@ "@semantic-release/git": "^7.0.8", "@semantic-release/npm": "^5.1.4", "@semantic-release/release-notes-generator": "^7.1.4", + "@types/benchmark": "^1.0.31", "@types/chai": "^4.1.7", "@types/chai-as-promised": "^7.1.0", "@types/express": "^4.16.1", @@ -55,6 +57,7 @@ "@types/sinon-chai": "^3.2.2", "@types/supertest": "^2.0.7", "@typescript-eslint/eslint-plugin": "^1.3.0", + "benchmark": "^2.1.4", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "coveralls": "^3.0.2", diff --git a/src/render/syntax.ts b/src/render/syntax.ts index 074469618..4ba09331c 100644 --- a/src/render/syntax.ts +++ b/src/render/syntax.ts @@ -44,7 +44,8 @@ export function evalExp (exp: string, scope: Scope): any { } export function evalValue (str: string, scope: Scope) { - str = str && str.trim() + if (!str) return null + str = str.trim() if (!str) return undefined if (lexical.isLiteral(str)) { diff --git a/test/stub/mockfs.ts b/test/stub/mockfs.ts index 3806f7c25..60aee1445 100644 --- a/test/stub/mockfs.ts +++ b/test/stub/mockfs.ts @@ -14,7 +14,6 @@ export function mock (options: { [path: string]: (string | fileDescriptor) }) { : val as fileDescriptor }) fs.readFile = async function (path) { - console.log('mock fs read called', path) const file = files[path] if (file === undefined) throw new Error('ENOENT') if (file.mode === '0000') throw new Error('EACCES') @@ -22,7 +21,6 @@ export function mock (options: { [path: string]: (string | fileDescriptor) }) { } fs.exists = async function (path: string) { - console.log('mock fs exists called', path) return !!files[path] } } diff --git a/test/unit/util/error.ts b/test/unit/util/error.ts index ebab9c5f9..8a17c582c 100644 --- a/test/unit/util/error.ts +++ b/test/unit/util/error.ts @@ -45,7 +45,6 @@ describe('error', function () { it('should contain stack in err.stack', async function () { const err = await expect(engine.parseAndRender('{% . a %}')).be.rejected expect(err.message).to.contain('illegal tag syntax') - console.log(err.stack) expect(err.stack).to.contain('at Liquid.parse') }) describe('captureStackTrace compatibility', function () { @@ -148,8 +147,6 @@ describe('error', function () { 'RenderError' ] const err = await expect(engine.parseAndRender(html)).be.rejected - console.log(err.message) - console.log(err.stack) expect(err.message).to.equal(`intended render error, file:${path.resolve('/throwing-tag.html')}, line:4, col:2`) expect(err.stack).to.contain(message.join('\n')) expect(err.name).to.equal('RenderError') diff --git a/tsconfig.json b/tsconfig.json index 3cd2d81b7..131ac40c5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,10 +11,11 @@ "baseUrl": ".", "paths": { "src/fs": ["src/fs/node"], + "benchmark": ["node_modules/benchmark"], "src/*": ["src/*"], "test/*": ["test/*"] } }, - "include": [ "src", "test" ], + "all": true, "exclude": [ "node_modules", "dist" ] }