Compare commits

...
83 Commits
Author SHA1 Message Date
semantic-release-bot 49c45a463b chore(release): 8.4.1 [skip ci]
## [8.4.1](https://github.com/harttle/liquidjs/compare/v8.4.0...v8.4.1) (2019-07-22)

### Bug Fixes

* some filters on undefined variable throws, [#140](https://github.com/harttle/liquidjs/issues/140) ([6e6ea0a](https://github.com/harttle/liquidjs/commit/6e6ea0a))
2019-07-22 08:52:48 +00:00
harttle cef5d1aa1a chore: remove support for node_js 6 2019-07-22 16:48:48 +08:00
harttle 6e6ea0ae58 fix: some filters on undefined variable throws, #140 2019-07-22 16:48:48 +08:00
harttle dc9a6e019e doc: make the purpose clear #139 , add plugin list 2019-07-22 02:04:53 +08:00
semantic-release-bot 5adebaa710 chore(release): 8.4.0 [skip ci]
# [8.4.0](https://github.com/harttle/liquidjs/compare/v8.3.0...v8.4.0) (2019-07-06)

### Features

* at_least, at_most, sort_naturual for [#132](https://github.com/harttle/liquidjs/issues/132) ([e6f5f1c](https://github.com/harttle/liquidjs/commit/e6f5f1c))
2019-07-06 06:54:15 +00:00
harttle 34d5af25a4 chore: trying to fix TS4053 2019-07-06 14:49:39 +08:00
harttle 88c89fe3b3 style: introduce @typescript-eslint/recommended 2019-07-06 14:33:34 +08:00
harttle e6f5f1cd85 feat: at_least, at_most, sort_naturual for #132 2019-07-05 12:09:00 +08:00
semantic-release-bot 570400ebcc chore(release): 8.3.0 [skip ci]
# [8.3.0](https://github.com/harttle/liquidjs/compare/v8.2.4...v8.3.0) (2019-06-27)

### Features

* fs option implemented by [#138](https://github.com/harttle/liquidjs/issues/138) ([3f5e23c](https://github.com/harttle/liquidjs/commit/3f5e23c))
2019-06-27 01:47:56 +00:00
Jun Yang 3f5e23c42b feat: fs option implemented by #138 2019-06-27 09:42:29 +08:00
Jun Yang 96c061bee0 style: remove semi 2019-06-27 09:32:51 +08:00
Patrick MalouinandJun Yang edb2cc1f37 Expose fs as an option to override the default implementations 2019-06-27 09:25:17 +08:00
harttle 2ae8c37a4f chore: add @semantic-release/github for notify 2019-06-17 17:24:07 +08:00
semantic-release-bot 0bc5908f17 chore(release): 8.2.4 [skip ci]
## [8.2.4](https://github.com/harttle/liquidjs/compare/v8.2.3...v8.2.4) (2019-06-17)

### Performance Improvements

* improve getTemplate() when cache is enabled ([1ffba2b](https://github.com/harttle/liquidjs/commit/1ffba2b))
2019-06-17 03:10:34 +00:00
Patrick MalouinandJun Yang 1ffba2bc76 perf: improve getTemplate() when cache is enabled
Checking for file existence is unnecessary when cache contains entry.
2019-06-17 11:01:02 +08:00
semantic-release-bot bbebb5515c chore(release): 8.2.3 [skip ci]
## [8.2.3](https://github.com/harttle/liquidjs/compare/v8.2.2...v8.2.3) (2019-05-19)

### Bug Fixes

* reverse filter not pure, see [#126](https://github.com/harttle/liquidjs/issues/126) ([505c408](https://github.com/harttle/liquidjs/commit/505c408))
2019-05-19 06:10:34 +00:00
Jun Yang b14430a161 style: test/integration/builtin/filters/array.ts 2019-05-19 14:05:28 +08:00
Jun Yang 505c408a85 fix: reverse filter not pure, see #126 2019-05-19 13:58:11 +08:00
Jun Yang 224679a7d7 chore: speedup unit test on windows 2019-05-19 13:00:51 +08:00
semantic-release-bot 2cb4bffd3e chore(release): 8.2.2 [skip ci]
## [8.2.2](https://github.com/harttle/liquidjs/compare/v8.2.1...v8.2.2) (2019-05-12)

### Bug Fixes

* date from integer, [#125](https://github.com/harttle/liquidjs/issues/125) ([fdf0043](https://github.com/harttle/liquidjs/commit/fdf0043))
* pass drops directly to filters/tags ([bef2909](https://github.com/harttle/liquidjs/commit/bef2909))
2019-05-12 15:02:33 +00:00
harttle c940e6c997 chore: try to make unit test locale independent 2019-05-12 22:58:29 +08:00
harttle d8ba009773 chore: fix building 2019-05-12 21:29:58 +08:00
harttle fdf0043b9c fix: date from integer, #125 2019-05-12 21:03:35 +08:00
harttle bef290923c fix: pass drops directly to filters/tags 2019-05-12 20:03:32 +08:00
semantic-release-bot 4282accaa2 chore(release): 8.2.1 [skip ci]
## [8.2.1](https://github.com/harttle/liquidjs/compare/v8.2.0...v8.2.1) (2019-04-26)

### Bug Fixes

* `default` filter is not working with an empty string, [#122](https://github.com/harttle/liquidjs/issues/122) ([6075c0a](https://github.com/harttle/liquidjs/commit/6075c0a))
2019-04-26 16:32:20 +00:00
harttle 6075c0a54f fix: default filter is not working with an empty string, #122 2019-04-27 00:26:17 +08:00
semantic-release-bot 87489f6098 chore(release): 8.2.0 [skip ci]
# [8.2.0](https://github.com/harttle/liquidjs/compare/v8.1.0...v8.2.0) (2019-04-17)

### Features

* pass context to filters ([00bc1ef](https://github.com/harttle/liquidjs/commit/00bc1ef))
2019-04-17 02:51:13 +00:00
harttle 6c5dc336e7 chore: fix linting, working on #120 2019-04-17 10:45:02 +08:00
harttle 00bc1efe6a feat: pass context to filters 2019-04-17 10:24:55 +08:00
harttle 6fb4796202 chore: set outDir to dist, see #119 2019-04-16 11:23:29 +08:00
semantic-release-bot 7483613b23 chore(release): 8.1.0 [skip ci]
# [8.1.0](https://github.com/harttle/liquidjs/compare/v8.0.3...v8.1.0) (2019-04-02)

### Features

* where filter, working on [#118](https://github.com/harttle/liquidjs/issues/118) ([daa0b57](https://github.com/harttle/liquidjs/commit/daa0b57))
2019-04-02 04:55:18 +00:00
harttle 7762dd9612 chore: fix linting 2019-04-02 12:10:35 +08:00
Jun Yang daa0b574fc feat: where filter, working on #118
doc: https://shopify.github.io/liquid/filters/where/
ruby impl: https://github.com/Shopify/liquid/blob/14cd011cb5eef042af192ad5294c8f0e1ae89f06/lib/liquid/standardfilters.rb
2019-04-02 00:46:32 +08:00
semantic-release-bot a084183246 chore(release): 8.0.3 [skip ci]
## [8.0.3](https://github.com/harttle/liquidjs/compare/v8.0.2...v8.0.3) (2019-04-01)

### Bug Fixes

* slice filter on negative `begin`, [#117](https://github.com/harttle/liquidjs/issues/117) ([eadb6f3](https://github.com/harttle/liquidjs/commit/eadb6f3))
2019-04-01 03:04:24 +00:00
harttle eadb6f3711 fix: slice filter on negative begin, #117 2019-04-01 10:57:08 +08:00
semantic-release-bot dba26f24e6 chore(release): 8.0.2 [skip ci]
## [8.0.2](https://github.com/harttle/liquidjs/compare/v8.0.1...v8.0.2) (2019-03-25)

### Performance Improvements

* use polymophism instead duck test ([82d7673](https://github.com/harttle/liquidjs/commit/82d7673))
2019-03-25 15:15:04 +00:00
Jun Yang 722da6a7ee doc: update README 2019-03-25 23:01:04 +08:00
harttle 82d7673554 perf: use polymophism instead duck test 2019-03-25 20:11:23 +08:00
harttle 64dd057552 refactor: Context#propertyAccessSeq => parseProp 2019-03-25 10:36:23 +08:00
harttle 45e3c2bb8e refactor: switch Context <-> Scope concepts 2019-03-25 10:36:23 +08:00
Jun Yang 76345a64c3 doc: update README.md 2019-03-23 22:28:33 +08:00
semantic-release-bot 9e69652655 chore(release): 8.0.1 [skip ci]
## [8.0.1](https://github.com/harttle/liquidjs/compare/v8.0.0...v8.0.1) (2019-03-22)

### Bug Fixes

* incorrect scope when using assign with for, fixes [#115](https://github.com/harttle/liquidjs/issues/115) ([defbb58](https://github.com/harttle/liquidjs/commit/defbb58))
2019-03-22 12:48:41 +00:00
harttle defbb58e66 fix: incorrect scope when using assign with for, fixes #115
* remove AssignScope, CaptureScope, IncrementScope, DecrementScope concepts
* introduce Scope.environments for decrement/increment
* assign to scopes[0] for assign/capture
2019-03-22 20:38:54 +08:00
ssendevandJun Yang b7b92c63cf async Register Tags example 2019-03-21 20:46:21 +08:00
ssendevandJun Yang 265534b7b7 Fix Register Tags example 2019-03-21 20:46:21 +08:00
semantic-release-bot 84ea2c7488 chore(release): 8.0.0 [skip ci]
# [8.0.0](https://github.com/harttle/liquidjs/compare/v7.5.1...v8.0.0) (2019-03-10)

### Code Refactoring

* use camelCase for JavaScript APIs ([64e0c87](https://github.com/harttle/liquidjs/commit/64e0c87))

### Features

* promise support for drops, working on [#65](https://github.com/harttle/liquidjs/issues/65) ([4a8088d](https://github.com/harttle/liquidjs/commit/4a8088d))

### BREAKING CHANGES

* Options and method names in JavaScript API are now renamed to cammelCase, for a complete list see #109
2019-03-10 11:28:39 +00:00
Jun Yang 64e0c876e9 refactor: use camelCase for JavaScript APIs
BREAKING CHANGE: Options and method names in JavaScript API are now renamed to cammelCase, for a complete list see #109
2019-03-10 19:21:09 +08:00
Jun Yang 4a8088d4e4 feat: promise support for drops, working on #65 2019-03-10 18:05:52 +08:00
harttle ad0930f152 test: cover parseFilters in template/value.ts 2019-03-06 01:46:35 +08:00
harttle 998832c772 refactor: mv custom filter test 2019-03-06 01:40:31 +08:00
semantic-release-bot 4ba224043b chore(release): 7.5.1 [skip ci]
## [7.5.1](https://github.com/harttle/liquidjs/compare/v7.5.0...v7.5.1) (2019-03-05)

### Bug Fixes

* named params for filters, working on [#113](https://github.com/harttle/liquidjs/issues/113) ([5ffc904](https://github.com/harttle/liquidjs/commit/5ffc904))
2019-03-05 17:39:56 +00:00
harttle 5ffc904f80 fix: named params for filters, working on #113 2019-03-06 01:30:47 +08:00
semantic-release-bot 08646f75b0 chore(release): 7.5.0 [skip ci]
# [7.5.0](https://github.com/harttle/liquidjs/compare/v7.4.0...v7.5.0) (2019-03-01)

### Features

* tablerowloop object ([3647305](https://github.com/harttle/liquidjs/commit/3647305))
2019-03-01 22:06:31 +00:00
Jun Yang 3647305dcf feat: tablerowloop object
See: https://www.rubydoc.info/gems/liquid/Liquid/TablerowloopDrop
2019-03-02 06:00:30 +08:00
Jun Yang cca0306f5b chore: fix mocha pattern for windows
See: https://github.com/mochajs/mocha/issues/2895
2019-03-02 03:57:38 +08:00
Jun Yang eeeacc4492 chore: fix rollup.config.ts 2019-03-02 03:42:57 +08:00
Jun Yang d44e1d727a chore: fix test on windows 10 2019-03-02 03:36:33 +08:00
semantic-release-bot f960904df4 chore(release): 7.4.0 [skip ci]
# [7.4.0](https://github.com/harttle/liquidjs/compare/v7.3.1...v7.4.0) (2019-02-28)

### Bug Fixes

* math filters now return number, resolves [#110](https://github.com/harttle/liquidjs/issues/110) ([b4acdb4](https://github.com/harttle/liquidjs/commit/b4acdb4))

### Features

* exported Drop interface for [#107](https://github.com/harttle/liquidjs/issues/107) ([7bee9fc](https://github.com/harttle/liquidjs/commit/7bee9fc)), closes [#109](https://github.com/harttle/liquidjs/issues/109)
2019-02-28 05:13:55 +00:00
harttle b4acdb463a fix: math filters now return number, resolves #110
* fix linting
* numbers are no longer fixed:
    i.e. {{0.3 | minus 0.1}} now renders as 0.19999
    Stick to JavaScript number behavior, see: 8.0.0
2019-02-28 13:02:51 +08:00
harttle 21d674e22b chore: fix benchmark npm script 2019-02-28 00:05:37 +08:00
harttle 7bee9fc92d feat: exported Drop interface for #107
Deprecate snake_cased options and APIs, sed #109
2019-02-28 00:05:08 +08:00
harttle b69c3a3203 refactor: IDrop interface => abstract method 2019-02-28 00:05:08 +08:00
harttle d5b9916b2f refactor: Drop.prototype.value => valueOf 2019-02-28 00:05:08 +08:00
semantic-release-bot d0118402e4 chore(release): 7.3.1 [skip ci]
## [7.3.1](https://github.com/harttle/liquidjs/compare/v7.3.0...v7.3.1) (2019-02-25)

### Bug Fixes

* **#108:** remove absolute path in emitted d.ts ([53a835a](https://github.com/harttle/liquidjs/commit/53a835a)), closes [#108](https://github.com/harttle/liquidjs/issues/108)
2019-02-25 17:03:07 +00:00
harttle 31bc71d472 chore: fix linting 2019-02-26 00:56:12 +08:00
harttle 53a835ab54 fix(#108): remove absolute path in emitted d.ts 2019-02-26 00:43:30 +08:00
harttle 75b3e15ca9 chore: fix rollup.config.ts types 2019-02-25 00:12:31 +08:00
harttle f34573ee21 chore: stick with es6 for testing 2019-02-24 23:42:44 +08:00
harttle 875f71d5f2 chore: a simple liquid_method_missing case for #97 2019-02-24 23:08:53 +08:00
harttle 407ac6d0a4 refactor: remove /dist from repo 2019-02-24 23:03:12 +08:00
semantic-release-bot f9f35ebd6c chore(release): 7.3.0 [skip ci]
# [7.3.0](https://github.com/harttle/liquidjs/compare/v7.2.2...v7.3.0) (2019-02-24)

### Features

* nil/null/empty/blank literals, resolves [#102](https://github.com/harttle/liquidjs/issues/102) ([88c9e96](https://github.com/harttle/liquidjs/commit/88c9e96))
2019-02-24 13:55:31 +00:00
harttle 88c9e96392 feat: nil/null/empty/blank literals, resolves #102 2019-02-24 21:50:01 +08:00
semantic-release-bot 54b2adce12 chore(release): 7.2.2 [skip ci]
## [7.2.2](https://github.com/harttle/liquidjs/compare/v7.2.1...v7.2.2) (2019-02-23)

### Bug Fixes

* filters break when argument contains [()|, fixes [#89](https://github.com/harttle/liquidjs/issues/89) ([e977669](https://github.com/harttle/liquidjs/commit/e977669))
2019-02-23 22:32:04 +00:00
harttle 542968e133 refactor: remove unused lexical regexps 2019-02-24 06:26:33 +08:00
harttle 3b7351d9a0 chore: fix linting 2019-02-24 05:03:59 +08:00
harttle e977669118 fix: filters break when argument contains [()|, fixes #89 2019-02-24 04:48:42 +08:00
harttle 279458c670 test: add benchmark 2019-02-23 21:57:02 +08:00
semantic-release-bot 37d12a14eb chore(release): 7.2.1 [skip ci]
## [7.2.1](https://github.com/harttle/liquidjs/compare/v7.2.0...v7.2.1) (2019-02-22)

### Bug Fixes

* default length for truncate and truncatewords ([56c7992](https://github.com/harttle/liquidjs/commit/56c7992))
2019-02-22 19:56:16 +00:00
harttle 3cf01603e8 chore: fix linting 2019-02-23 03:50:45 +08:00
harttle 56c7992b76 fix: default length for truncate and truncatewords 2019-02-23 03:11:07 +08:00
harttle 5b6100d12b refactor: strictly typed 2019-02-23 00:49:54 +08:00
Jun YangandGitHub 51f7e66f60 Update CHANGELOG.md 2019-02-21 01:19:25 +08:00
Jun YangandGitHub 952ab6e73b Update CHANGELOG.md 2019-02-21 01:16:32 +08:00
224 changed files with 6248 additions and 10290 deletions
+11 -2
View File
@@ -1,5 +1,5 @@
{
"extends": "standard",
"extends": ["standard", "plugin:@typescript-eslint/recommended"],
"env": {
"mocha": true,
"es6": true,
@@ -17,6 +17,15 @@
"no-var": 2,
"prefer-const": 2,
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }]
"indent": "off",
"@typescript-eslint/indent": ["error", 2],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-object-literal-type-assertion": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/interface-name-prefix": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"import/export": "off",
"@typescript-eslint/no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": false }]
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ coverage/
# modules
node_modules/
build/
dist/
# editors
.*.swp
-1
View File
@@ -2,7 +2,6 @@ language: node_js
node_js:
- "10"
- "8"
- "6"
jobs:
include:
- stage: test
+160
View File
@@ -1,3 +1,159 @@
## [8.4.1](https://github.com/harttle/liquidjs/compare/v8.4.0...v8.4.1) (2019-07-22)
### Bug Fixes
* some filters on undefined variable throws, [#140](https://github.com/harttle/liquidjs/issues/140) ([6e6ea0a](https://github.com/harttle/liquidjs/commit/6e6ea0a))
# [8.4.0](https://github.com/harttle/liquidjs/compare/v8.3.0...v8.4.0) (2019-07-06)
### Features
* at_least, at_most, sort_naturual for [#132](https://github.com/harttle/liquidjs/issues/132) ([e6f5f1c](https://github.com/harttle/liquidjs/commit/e6f5f1c))
# [8.3.0](https://github.com/harttle/liquidjs/compare/v8.2.4...v8.3.0) (2019-06-27)
### Features
* fs option implemented by [#138](https://github.com/harttle/liquidjs/issues/138) ([3f5e23c](https://github.com/harttle/liquidjs/commit/3f5e23c))
## [8.2.4](https://github.com/harttle/liquidjs/compare/v8.2.3...v8.2.4) (2019-06-17)
### Performance Improvements
* improve getTemplate() when cache is enabled ([1ffba2b](https://github.com/harttle/liquidjs/commit/1ffba2b))
## [8.2.3](https://github.com/harttle/liquidjs/compare/v8.2.2...v8.2.3) (2019-05-19)
### Bug Fixes
* reverse filter not pure, see [#126](https://github.com/harttle/liquidjs/issues/126) ([505c408](https://github.com/harttle/liquidjs/commit/505c408))
## [8.2.2](https://github.com/harttle/liquidjs/compare/v8.2.1...v8.2.2) (2019-05-12)
### Bug Fixes
* date from integer, [#125](https://github.com/harttle/liquidjs/issues/125) ([fdf0043](https://github.com/harttle/liquidjs/commit/fdf0043))
* pass drops directly to filters/tags ([bef2909](https://github.com/harttle/liquidjs/commit/bef2909))
## [8.2.1](https://github.com/harttle/liquidjs/compare/v8.2.0...v8.2.1) (2019-04-26)
### Bug Fixes
* `default` filter is not working with an empty string, [#122](https://github.com/harttle/liquidjs/issues/122) ([6075c0a](https://github.com/harttle/liquidjs/commit/6075c0a))
# [8.2.0](https://github.com/harttle/liquidjs/compare/v8.1.0...v8.2.0) (2019-04-17)
### Features
* pass context to filters ([00bc1ef](https://github.com/harttle/liquidjs/commit/00bc1ef))
# [8.1.0](https://github.com/harttle/liquidjs/compare/v8.0.3...v8.1.0) (2019-04-02)
### Features
* where filter, working on [#118](https://github.com/harttle/liquidjs/issues/118) ([daa0b57](https://github.com/harttle/liquidjs/commit/daa0b57))
## [8.0.3](https://github.com/harttle/liquidjs/compare/v8.0.2...v8.0.3) (2019-04-01)
### Bug Fixes
* slice filter on negative `begin`, [#117](https://github.com/harttle/liquidjs/issues/117) ([eadb6f3](https://github.com/harttle/liquidjs/commit/eadb6f3))
## [8.0.2](https://github.com/harttle/liquidjs/compare/v8.0.1...v8.0.2) (2019-03-25)
### Performance Improvements
* use polymophism instead duck test ([82d7673](https://github.com/harttle/liquidjs/commit/82d7673))
## [8.0.1](https://github.com/harttle/liquidjs/compare/v8.0.0...v8.0.1) (2019-03-22)
### Bug Fixes
* incorrect scope when using assign with for, fixes [#115](https://github.com/harttle/liquidjs/issues/115) ([defbb58](https://github.com/harttle/liquidjs/commit/defbb58))
# [8.0.0](https://github.com/harttle/liquidjs/compare/v7.5.1...v8.0.0) (2019-03-10)
### Code Refactoring
* use camelCase for JavaScript APIs ([64e0c87](https://github.com/harttle/liquidjs/commit/64e0c87))
### Features
* promise support for drops, working on [#65](https://github.com/harttle/liquidjs/issues/65) ([4a8088d](https://github.com/harttle/liquidjs/commit/4a8088d))
### BREAKING CHANGES
* Options and method names in JavaScript API are now renamed to cammelCase, for a complete list see #109
## [7.5.1](https://github.com/harttle/liquidjs/compare/v7.5.0...v7.5.1) (2019-03-05)
### Bug Fixes
* named params for filters, working on [#113](https://github.com/harttle/liquidjs/issues/113) ([5ffc904](https://github.com/harttle/liquidjs/commit/5ffc904))
# [7.5.0](https://github.com/harttle/liquidjs/compare/v7.4.0...v7.5.0) (2019-03-01)
### Features
* tablerowloop object ([3647305](https://github.com/harttle/liquidjs/commit/3647305))
# [7.4.0](https://github.com/harttle/liquidjs/compare/v7.3.1...v7.4.0) (2019-02-28)
### Bug Fixes
* math filters now return number, resolves [#110](https://github.com/harttle/liquidjs/issues/110) ([b4acdb4](https://github.com/harttle/liquidjs/commit/b4acdb4))
### Features
* exported Drop interface for [#107](https://github.com/harttle/liquidjs/issues/107) ([7bee9fc](https://github.com/harttle/liquidjs/commit/7bee9fc)), closes [#109](https://github.com/harttle/liquidjs/issues/109)
## [7.3.1](https://github.com/harttle/liquidjs/compare/v7.3.0...v7.3.1) (2019-02-25)
### Bug Fixes
* **#108:** remove absolute path in emitted d.ts ([53a835a](https://github.com/harttle/liquidjs/commit/53a835a)), closes [#108](https://github.com/harttle/liquidjs/issues/108)
# [7.3.0](https://github.com/harttle/liquidjs/compare/v7.2.2...v7.3.0) (2019-02-24)
### Features
* nil/null/empty/blank literals, resolves [#102](https://github.com/harttle/liquidjs/issues/102) ([88c9e96](https://github.com/harttle/liquidjs/commit/88c9e96))
## [7.2.2](https://github.com/harttle/liquidjs/compare/v7.2.1...v7.2.2) (2019-02-23)
### Bug Fixes
* filters break when argument contains [()|, fixes [#89](https://github.com/harttle/liquidjs/issues/89) ([e977669](https://github.com/harttle/liquidjs/commit/e977669))
## [7.2.1](https://github.com/harttle/liquidjs/compare/v7.2.0...v7.2.1) (2019-02-22)
### Bug Fixes
* default length for truncate and truncatewords ([56c7992](https://github.com/harttle/liquidjs/commit/56c7992))
# [7.2.0](https://github.com/harttle/liquidjs/compare/v7.1.0...v7.2.0) (2019-02-20)
@@ -5,6 +161,10 @@
* override output/tag delimiter, fixes [#54](https://github.com/harttle/liquidjs/issues/54) ([d20a043](https://github.com/harttle/liquidjs/commit/d20a043))
### BREAKING CHANGES
* `trim_value_left` option renamed to `trim_output_left`, `trim_value_right` option renamed to `trim_output_right`
# [7.1.0](https://github.com/harttle/liquidjs/compare/v7.0.2...v7.1.0) (2019-02-20)
+55 -70
View File
@@ -12,51 +12,61 @@
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://github.com/harttle/liquidjs)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/harttle/liquidjs)
This is a liquid implementation for both Node.js and browsers. Website: <http://harttle.github.io/liquidjs/>, Live Demo: <https://jsfiddle.net/6u40xbzs/>
A [shopify][shopify/liquid] compatible [Liquid][tutorial] template engine in pure JavaScript. Install via npm:
**Features**
```bash
npm install --save liquidjs
```
* Fully compatible to [shopify][shopify/liquid], with all [tags][tags] and [filters][filters] implemented
* Support layout(extend) and include syntax
* In pure JavaScript with Promise-based API
Or include the UMD build (You may need a [Promise polyfill][pp] for Node.js &lt; 4 and ES5 browsers like [IE and Android UC][caniuse-promises]):
**Differences**
```html
<script src="//unpkg.com/liquidjs/dist/liquid.min.js"></script> <!--for production-->
<script src="//unpkg.com/liquidjs/dist/liquid.js"></script> <!--for development-->
```
Though being compatible with [Ruby Liquid](https://github.com/shopify/liquid) is one of our priorities, there're still certain differences. You may need some configuration to get it compatible in these senarios:
**The purpose of this repo** is to provide a standard Liquid implementation for the JavaScript community.
All features, filters and tags in [shopify/liquid](https://github.com/Shopify/liquid) are supposed to be supported here,
though there are still some differences:
* Dynamic file locating (enabled by default), which means layout/partial name can be an variable in liquidjs. See [#51](https://github.com/harttle/liquidjs/issues/51).
* Dynamic file locating (enabled by default), that means layout/partial names are treated as variables in liquidjs. See [#51](https://github.com/harttle/liquidjs/issues/51).
* Truthy and Falsy. All values except `undefined`, `null`, `false` are truthy, whereas in Ruby Liquid all except `nil` and `false` are truthy. See [#26](https://github.com/harttle/liquidjs/pull/26).
* Number Rendering. Since JavaScript do not distinguish `float` and `integer`, we cannot either convert between them nor render regarding to their type. See [#59](https://github.com/harttle/liquidjs/issues/59).
* [.to_liquid()](https://github.com/Shopify/liquid/wiki/Introduction-to-Drops) has a `.toLiquid()` alias and and the JavaScript `.toString()` is aliased to `.to_s()`.
* [.to_s()](https://www.rubydoc.info/gems/liquid/Liquid/Drop) uses `JSON.prototype.stringify` as default, rather than Ruby's inspect.
* Number. In JavaScript we cannot distinguish or convert between `float` and `integer`, see [#59](https://github.com/harttle/liquidjs/issues/59). And when applied `size` filter, numbers always return 0, which is 8 for integer in ruby, cause they do not have a `length` property.
* [.to_liquid()](https://github.com/Shopify/liquid/wiki/Introduction-to-Drops) is replaced by `.toLiquid()`
* [.to_s()](https://www.rubydoc.info/gems/liquid/Liquid/Drop) is replaced by JavaScript `.toString()`
Features that available on shopify website but not on shopify/liquid repo will not be implemented in this repo,
but there're some plugins available (feel free to add yours):
* color filters: https://github.com/harttle/liquidjs-color-filters
* sections tags (WIP): https://github.com/harttle/liquidjs-section-tags
## TOC
* Usage
* Get Started
* [Render from String](#render-from-string)
* [Render from File](#render-from-file)
* [Use with Express.js](#use-with-expressjs)
* [Use in Browser](#use-in-browser)
* [Include Partials](#include-partials)
* [Layout Templates (Extends)](#layout-templates-extends)
* API Spec
* [Constructor Options](#options)
* Demos
* Node.js: [/demo/node/](demo/node/)
* Browser: <https://jsfiddle.net/6u40xbzs/>, [/demo/browser/](demo/browser/).
* Express.js: [/demo/express/](demo/express/)
* TypeScript: [/demo/typescript/](demo/typescript/)
* React JS: [/demo/reactjs/](demo/reactjs/)
* Advanced
* [Options](#options)
* [Register Filters](#register-filters), [Builtin Filters](https://github.com/harttle/liquidjs/wiki/Builtin-Filters)
* [Register Tags](#register-tags), [Builtin Tags](https://github.com/harttle/liquidjs/wiki/Builtin-Tags)
* [Operators](https://github.com/harttle/liquidjs/wiki/Operators)
* [Whitespace Control](https://github.com/harttle/liquidjs/wiki/Whitespace-Control)
* [Plugin API](#plugin-api)
* [Contribute Guidelines](#contribute-guidelines)
## Render from String
Install as Node.js dependency:
```bash
# You'll need a promise-polyfill for Node.js < 4
npm install --save liquidjs
```
Parse and Render:
Just render a template string with a context:
```javascript
var Liquid = require('liquidjs');
@@ -64,35 +74,27 @@ var engine = new Liquid();
engine
.parseAndRender('{{name | capitalize}}', {name: 'alice'})
.then(console.log);
// outputs 'Alice'
.then(console.log); // outputs 'Alice'
```
Caching templates:
Caching parsed templates:
```javascript
var tpl = engine.parse('{{name | capitalize}}');
engine
.render(tpl, {name: 'alice'})
.then(console.log);
// outputs 'Alice'
.then(console.log); // outputs 'Alice'
```
## Render from File
```javascript
var engine = new Liquid({
root: path.resolve(__dirname, 'views/'), // dirs to lookup layouts/includes
extname: '.liquid' // the extname used for layouts/includes, defaults ""
root: path.resolve(__dirname, 'views/'), // root for layouts/includes lookup
extname: '.liquid' // used for layouts/includes, defaults ""
});
engine.renderFile("hello.liquid", {name: 'alice'})
.then(console.log) // outputs "Alice"
// which is equivalent to:
engine
.renderFile("hello", {name: 'alice'})
.renderFile("hello", {name: 'alice'}) // will read and render `views/hello.liquid`
.then(console.log) // outputs "Alice"
```
@@ -105,24 +107,9 @@ app.set('views', './views'); // specify the views directory
app.set('view engine', 'liquid'); // set to default
```
[Here](demo/express/)'s an Express demo. When used with Express.js,
Express [`views`][express-views] will be included when looking up
[`views`][express-views] in express.js will be included when looking up
partials(includes and layouts).
## Use in Browser
You can get a dist file for browsers from
* [Releases][releases] page for liquidjs, or
* unpkg.com: <https://unpkg.com/liquidjs/dist/liquid.min.js>
Here's the demo:
* JSFiddle: <https://jsfiddle.net/6u40xbzs/>
* Demo directory: [/demo/browser/](demo/browser/).
Note: For [IE and Android UC][caniuse-promises] browser, you will need a [Promise polyfill][pp].
## Include Partials
```
@@ -182,25 +169,27 @@ Defaults to `["."]`
* `dynamicPartials`: if set, treat `<filepath>` parameter in `{%include filepath %}`, `{%layout filepath%}` as a variable, otherwise as a literal value. Defaults to `true`.
* `strict_filters` is used to enable strict filter existence. If set to `false`, undefined filters will be rendered as empty string. Otherwise, undefined filters will cause an exception. Defaults to `false`.
* `strictFilters` is used to enable strict filter existence. If set to `false`, undefined filters will be rendered as empty string. Otherwise, undefined filters will cause an exception. Defaults to `false`.
* `strict_variables` is used to enable strict variable derivation.
* `strictVariables` is used to enable strict variable derivation.
If set to `false`, undefined variables will be rendered as empty string.
Otherwise, undefined variables will cause an exception. Defaults to `false`.
* `trim_tag_right` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of tags (`{% %}`) until `\n` (inclusive). Defaults to `false`.
* `trimTagRight` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of tags (`{% %}`) until `\n` (inclusive). Defaults to `false`.
* `trim_tag_left` is similiar to `trim_tag_right`, whereas the `\n` is exclusive. Defaults to `false`. See [Whitespace Control][whitespace control] for details.
* `trimTagLeft` is similiar to `trimTagRight`, whereas the `\n` is exclusive. Defaults to `false`. See [Whitespace Control][whitespace control] for details.
* `trim_output_right` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of values (`{{ }}`) until `\n` (inclusive). Defaults to `false`.
* `trimOutputRight` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of values (`{{ }}`) until `\n` (inclusive). Defaults to `false`.
* `trim_output_left` is similiar to `trim_output_right`, whereas the `\n` is exclusive. Defaults to `false`. See [Whitespace Control][whitespace control] for details.
* `trimOutputLeft` is similiar to `trimOutputRight`, whereas the `\n` is exclusive. Defaults to `false`. See [Whitespace Control][whitespace control] for details.
* `tag_delimiter_left` and `tag_delimiter_right` are used to override the delimiter for liquid tags.
* `tagDelimiterLeft` and `tagDelimiterRight` are used to override the delimiter for liquid tags.
* `output_delimiter_left` and `output_delimiter_right` are used to override the delimiter for liquid outputs.
* `outputDelimiterLeft` and `outputDelimiterRight` are used to override the delimiter for liquid outputs.
* `greedy` is used to specify whether `trim_left`/`trim_right` is greedy. When set to `true`, all consecutive blank characters including `\n` will be trimed regardless of line breaks. Defaults to `true`.
* `greedy` is used to specify whether `trim*Left`/`trim*Right` is greedy. When set to `true`, all consecutive blank characters including `\n` will be trimed regardless of line breaks. Defaults to `true`.
* `fs` is used to override the default file-system module with a custom implementation.
## Register Filters
@@ -226,9 +215,9 @@ engine.registerTag('upper', {
parse: function(tagToken, remainTokens) {
this.str = tagToken.args; // name
},
render: function(scope, hash) {
var str = Liquid.evalValue(this.str, scope); // 'alice'
return Promise.resolve(str.toUpperCase()); // 'Alice'
render: async function(scope, hash) {
var str = await Liquid.evalValue(this.str, scope); // 'alice'
return str.toUpperCase() // 'Alice'
}
});
```
@@ -253,10 +242,6 @@ module.exports = function (Liquid) {
}
```
Plugin List:
* To add your plugin, contact me or simply send a PR.
## Contribute Guidelines
This repo uses [eslint](https://eslint.org/) to check code style, [semantic-release](https://github.com/semantic-release/semantic-release) to generate changelog and publish to npm and Github Releases.
@@ -269,7 +254,6 @@ This repo uses [eslint](https://eslint.org/) to check code style, [semantic-rele
[shopify/liquid]: https://shopify.github.io/liquid/
[jekyll]: http://jekyllrb.com/
[gh]: https://pages.github.com/
[releases]: https://github.com/harttle/liquidjs/releases
[any-promise]: https://github.com/kevinbeaty/any-promise
[test]: https://github.com/harttle/liquidjs/tree/master/test
[caniuse-promises]: http://caniuse.com/#feat=promises
@@ -278,3 +262,4 @@ This repo uses [eslint](https://eslint.org/) to check code style, [semantic-rele
[filters]: https://github.com/harttle/liquidjs/wiki/Builtin-Filters
[express-views]: http://expressjs.com/en/guide/using-template-engines.html
[pp]: https://github.com/taylorhakes/promise-polyfill
[tutorial]: https://shopify.github.io/liquid/basics/introduction/
+5
View File
@@ -0,0 +1,5 @@
{
"rules": {
"no-unused-expressions": "off"
}
}
+49
View File
@@ -0,0 +1,49 @@
import * as Benchmark from 'benchmark'
import Liquid from '../src/liquid'
import TagToken from '../src/parser/tag-token'
import Context from '../src/context/context'
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 (ctx: Context) {
const title = this.liquid.evalValue(this.content, ctx)
return `<h1>${title}</h1>`
}
})
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%}
<ul>
{% for todo in todos %}
<li>{{forloop.index}} - {{todo}}</li>
{% endfor %}
</ul>
`
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 })
})
}
+13
View File
@@ -0,0 +1,13 @@
import output from './output'
import tag from './tag'
import demo from './demo'
import layout from './layout'
async function main () {
await output()
await tag()
await demo()
await layout()
}
main()
+36
View File
@@ -0,0 +1,36 @@
import * as Benchmark from 'benchmark'
import Liquid from '../src/liquid'
const engineOptions = {
root: __dirname,
extname: '.liquid'
}
const engine = new Liquid(engineOptions)
const cachingEngine = new Liquid({
...engineOptions,
cache: true
})
const template = `
{% layout "./templates/layout.liquid" %}
{% block body %}a small body{% endblock %}
`
export default function () {
console.log('--- layout ---')
return new Promise(resolve => {
new Benchmark.Suite('layout')
.add('cache=false', {
defer: true,
fn: (d: any) => engine.parseAndRender(template, {}).then(x => d.resolve(x))
})
.add('cache=true', {
defer: true,
fn: (d: any) => cachingEngine.parseAndRender(template, {}).then(x => d.resolve(x))
})
.on('cycle', (event: any) => console.log(String(event.target)))
.on('complete', resolve)
.run({ 'async': true })
})
}
+26
View File
@@ -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))
}
}
+30
View File
@@ -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))
}
}
+2
View File
@@ -0,0 +1,2 @@
This is a barely empty layout with a body:
{% block body %}{% endblock %}
View File
-12
View File
@@ -1,12 +0,0 @@
declare const _default: {
'join': (v: any, arg: any) => any;
'last': (v: any) => any;
'first': (v: any) => any;
'map': (arr: any, arg: any) => any;
'reverse': (v: any) => any;
'sort': (v: any, arg: any) => any;
'size': (v: any) => any;
'slice': (v: any, begin: any, length: any) => any;
'uniq': (arr: any) => any;
};
export default _default;
-4
View File
@@ -1,4 +0,0 @@
declare const _default: {
'date': (v: any, arg: any) => any;
};
export default _default;
-8
View File
@@ -1,8 +0,0 @@
declare function escape(str: any): string;
declare const _default: {
'escape': typeof escape;
'escape_once': (str: any) => string;
'newline_to_br': (v: any) => any;
'strip_html': (v: any) => string;
};
export default _default;
-46
View File
@@ -1,46 +0,0 @@
declare const _default: {
'join': (v: any, arg: any) => any;
'last': (v: any) => any;
'first': (v: any) => any;
'map': (arr: any, arg: any) => any;
'reverse': (v: any) => any;
'sort': (v: any, arg: any) => any;
'size': (v: any) => any;
'slice': (v: any, begin: any, length: any) => any;
'uniq': (arr: any) => any;
'default': (v: any, arg: any) => any;
'date': (v: any, arg: any) => any;
'url_decode': (x: any) => any;
'url_encode': (x: any) => any;
'abs': (v: any) => number;
'ceil': (v: any) => number;
'divided_by': (v: any, arg: any) => number;
'floor': (v: any) => number;
'minus': (l: any, r: any) => any;
'modulo': (l: any, r: any) => any;
'round': (v: any, arg: any) => number;
'plus': (l: any, r: any) => any;
'times': (v: any, arg: any) => number;
'append': (v: any, arg: any) => any;
'prepend': (v: any, arg: any) => any;
'capitalize': (str: any) => string;
'concat': (v: any, arg: any) => any;
'lstrip': (v: any) => string;
'downcase': (v: any) => any;
'upcase': (str: any) => string;
'remove': (v: any, arg: any) => any;
'remove_first': (v: any, l: any) => any;
'replace': (v: any, pattern: any, replacement: any) => string;
'replace_first': (v: any, arg1: any, arg2: any) => string;
'rstrip': (str: any) => string;
'split': (v: any, arg: any) => string[];
'strip': (v: any) => string;
'strip_newlines': (v: any) => string;
'truncate': (v: any, l: any, o: any) => any;
'truncatewords': (v: any, l: any, o: any) => any;
'escape': (str: any) => string;
'escape_once': (str: any) => string;
'newline_to_br': (v: any) => any;
'strip_html': (v: any) => string;
};
export default _default;
-12
View File
@@ -1,12 +0,0 @@
declare const _default: {
'abs': (v: any) => number;
'ceil': (v: any) => number;
'divided_by': (v: any, arg: any) => number;
'floor': (v: any) => number;
'minus': (l: any, r: any) => any;
'modulo': (l: any, r: any) => any;
'round': (v: any, arg: any) => number;
'plus': (l: any, r: any) => any;
'times': (v: any, arg: any) => number;
};
export default _default;
-4
View File
@@ -1,4 +0,0 @@
declare const _default: {
'default': (v: any, arg: any) => any;
};
export default _default;
-20
View File
@@ -1,20 +0,0 @@
declare const _default: {
'append': (v: any, arg: any) => any;
'prepend': (v: any, arg: any) => any;
'capitalize': (str: any) => string;
'concat': (v: any, arg: any) => any;
'lstrip': (v: any) => string;
'downcase': (v: any) => any;
'upcase': (str: any) => string;
'remove': (v: any, arg: any) => any;
'remove_first': (v: any, l: any) => any;
'replace': (v: any, pattern: any, replacement: any) => string;
'replace_first': (v: any, arg1: any, arg2: any) => string;
'rstrip': (str: any) => string;
'split': (v: any, arg: any) => string[];
'strip': (v: any) => string;
'strip_newlines': (v: any) => string;
'truncate': (v: any, l: any, o: any) => any;
'truncatewords': (v: any, l: any, o: any) => any;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
'url_decode': (x: any) => any;
'url_encode': (x: any) => any;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
parse: (token: any) => void;
render: (scope: any) => Promise<string>;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
parse: (token: any, remainTokens: any) => void;
render: (scope: any) => Promise<any>;
};
export default _default;
-4
View File
@@ -1,4 +0,0 @@
declare const _default: {
render: () => Promise<never>;
};
export default _default;
-8
View File
@@ -1,8 +0,0 @@
import TagToken from 'src/parser/tag-token';
import Token from 'src/parser/token';
import Scope from 'src/scope/scope';
declare const _default: {
parse: (tagToken: TagToken, remainTokens: Token[]) => void;
render: (scope: Scope) => Promise<void>;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
parse: (tagToken: any, remainTokens: any) => void;
render: (scope: any) => any;
};
export default _default;
-4
View File
@@ -1,4 +0,0 @@
declare const _default: {
parse: (tagToken: any, remainTokens: any) => void;
};
export default _default;
-4
View File
@@ -1,4 +0,0 @@
declare const _default: {
render: () => Promise<never>;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
parse: (tagToken: any) => void;
render: (scope: any) => any;
};
export default _default;
-7
View File
@@ -1,7 +0,0 @@
import TagToken from 'src/parser/tag-token';
import Scope from 'src/scope/scope';
declare const _default: {
parse: (token: TagToken) => void;
render: (scope: Scope) => number;
};
export default _default;
-7
View File
@@ -1,7 +0,0 @@
declare function parse(tagToken: any, remainTokens: any): void;
declare function render(scope: any, hash: any): Promise<any>;
declare const _default: {
parse: typeof parse;
render: typeof render;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
parse: (tagToken: any, remainTokens: any) => void;
render: (scope: any) => any;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
parse: (token: any) => void;
render: (scope: any, hash: any) => Promise<any>;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
parse: (token: any) => void;
render: (scope: any) => any;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
import ITagImplOptions from 'src/template/tag/itag-impl-options';
declare const tags: {
[key: string]: ITagImplOptions;
};
export default tags;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
parse: (token: any, remainTokens: any) => void;
render: (scope: any, hash: any) => Promise<any>;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
parse: (tagToken: any, remainTokens: any) => void;
render: () => any;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
parse: (tagToken: any, remainTokens: any) => void;
render: (scope: any, hash: any) => Promise<string>;
};
export default _default;
-5
View File
@@ -1,5 +0,0 @@
declare const _default: {
parse: (tagToken: any, remainTokens: any) => void;
render: (scope: any) => any;
};
export default _default;
-3
View File
@@ -1,3 +0,0 @@
import IFS from './ifs';
declare const _default: IFS;
export default _default;
-5
View File
@@ -1,5 +0,0 @@
export default interface IFS {
exists: (filepath?: string) => Promise<boolean>;
readFile: (filepath: string) => Promise<string>;
resolve: (root: string, file: string, ext: string) => string;
}
-3
View File
@@ -1,3 +0,0 @@
import IFS from './ifs';
declare const fs: IFS;
export default fs;
-48
View File
@@ -1,48 +0,0 @@
export interface LiquidOptions {
/** `root` is a directory or an array of directories to resolve layouts and includes, as well as the filename passed in when calling `.renderFile()`. If an array, the files are looked up in the order they occur in the array. Defaults to `["."]` */
root?: string | string[];
/** `extname` is used to lookup the template file when filepath doesn't include an extension name. Eg: setting to `".html"` will allow including file by basename. Defaults to `""`. */
extname?: string;
/** `cache` indicates whether or not to cache resolved templates. Defaults to `false`. */
cache?: boolean;
/** `dynamicPartials`: if set, treat `<filepath>` parameter in `{%include filepath %}`, `{%layout filepath%}` as a variable, otherwise as a literal value. Defaults to `true`. */
dynamicPartials?: boolean;
/** `strict_filters` is used to enable strict filter existence. If set to `false`, undefined filters will be rendered as empty string. Otherwise, undefined filters will cause an exception. Defaults to `false`. */
strict_filters?: boolean;
/** `strict_variables` is used to enable strict variable derivation. If set to `false`, undefined variables will be rendered as empty string. Otherwise, undefined variables will cause an exception. Defaults to `false`. */
strict_variables?: boolean;
/** `trim_tag_right` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of tags (`{% %}`) until `\n` (inclusive). Defaults to `false`. */
trim_tag_right?: boolean;
/** `trim_tag_left` is similar to `trim_tag_right`, whereas the `\n` is exclusive. Defaults to `false`. See Whitespace Control for details. */
trim_tag_left?: boolean;
/** ``trim_output_right` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of values (`{{ }}`) until `\n` (inclusive). Defaults to `false`. */
trim_output_right?: boolean;
/** `trim_output_left` is similar to `trim_output_right`, whereas the `\n` is exclusive. Defaults to `false`. See Whitespace Control for details. */
trim_output_left?: boolean;
/** `tag_delimiter_left` and `tag_delimiter_right` are used to override the delimiter for liquid tags **/
tag_delimiter_left?: string;
tag_delimiter_right?: string;
/** `output_delimiter_left` and `output_delimiter_right` are used to override the delimiter for liquid outputs **/
output_delimiter_left?: string;
output_delimiter_right?: string;
/** `greedy` is used to specify whether `trim_left`/`trim_right` is greedy. When set to `true`, all consecutive blank characters including `\n` will be trimed regardless of line breaks. Defaults to `true`. */
greedy?: boolean;
}
export interface NormalizedOptions extends LiquidOptions {
root?: string[];
}
export interface NormalizedFullOptions extends NormalizedOptions {
root: string[];
extname: string;
cache: boolean;
dynamicPartials: boolean;
strict_filters: boolean;
strict_variables: boolean;
trim_tag_right: boolean;
trim_tag_left: boolean;
trim_output_right: boolean;
trim_output_left: boolean;
greedy: boolean;
}
export declare function normalize(options: LiquidOptions): NormalizedOptions;
export declare function applyDefault(options: NormalizedOptions): NormalizedFullOptions;
-2291
View File
File diff suppressed because it is too large Load Diff
-1
View File
File diff suppressed because one or more lines are too long
-30
View File
@@ -1,30 +0,0 @@
import Scope from './scope/scope';
import * as Types from './types';
import ITemplate from './template/itemplate';
import ITagImplOptions from './template/tag/itag-impl-options';
import { isTruthy, isFalsy, evalExp, evalValue } from './render/syntax';
import { LiquidOptions, NormalizedFullOptions } from './liquid-options';
export default class Liquid {
options: NormalizedFullOptions;
private cache;
private parser;
private renderer;
private tokenizer;
constructor(opts?: LiquidOptions);
parse(html: string, filepath?: string): any[];
render(tpl: Array<ITemplate>, ctx?: object, opts?: LiquidOptions): Promise<string>;
parseAndRender(html: string, ctx?: object, opts?: LiquidOptions): Promise<string>;
getTemplate(file: any, opts?: LiquidOptions): Promise<any>;
renderFile(file: any, ctx?: object, opts?: LiquidOptions): Promise<string>;
evalValue(str: string, scope: Scope): any;
registerFilter(name: any, filter: any): void;
registerTag(name: string, tag: ITagImplOptions): void;
plugin(plugin: any): any;
express(): (filePath: string, ctx: object, cb: (err: Error, html?: string) => void) => void;
static default: typeof Liquid;
static isTruthy: typeof isTruthy;
static isFalsy: typeof isFalsy;
static evalExp: typeof evalExp;
static evalValue: typeof evalValue;
static Types: typeof Types;
}
-2318
View File
File diff suppressed because it is too large Load Diff
-1
View File
File diff suppressed because one or more lines are too long
-2
View File
File diff suppressed because one or more lines are too long
-1
View File
File diff suppressed because one or more lines are too long
-6
View File
@@ -1,6 +0,0 @@
import Token from './token';
export default class DelimitedToken extends Token {
trimLeft: boolean;
trimRight: boolean;
constructor(raw: any, value: any, pos: any, input: any, file: any, line: any);
}
-4
View File
@@ -1,4 +0,0 @@
import Token from './token';
export default class HTMLToken extends Token {
constructor(str: any, begin: any, input: any, file: any, line: any);
}
-35
View File
@@ -1,35 +0,0 @@
export declare const quoted: RegExp;
export declare const quoteBalanced: RegExp;
export declare const integer: RegExp;
export declare const number: RegExp;
export declare const bool: RegExp;
export declare const identifier: RegExp;
export declare const subscript: RegExp;
export declare const literal: RegExp;
export declare const variable: RegExp;
export declare const rangeLimit: RegExp;
export declare const range: RegExp;
export declare const rangeCapture: RegExp;
export declare const value: RegExp;
export declare const hash: RegExp;
export declare const hashCapture: RegExp;
export declare const tagLine: RegExp;
export declare const literalLine: RegExp;
export declare const variableLine: RegExp;
export declare const numberLine: RegExp;
export declare const boolLine: RegExp;
export declare const quotedLine: RegExp;
export declare const rangeLine: RegExp;
export declare const integerLine: RegExp;
export declare const valueDeclaration: RegExp;
export declare const valueList: RegExp;
export declare const filter: RegExp;
export declare const filterCapture: RegExp;
export declare const filterLine: RegExp;
export declare const operators: RegExp[];
export declare function isInteger(str: any): boolean;
export declare function isLiteral(str: any): boolean;
export declare function isRange(str: any): boolean;
export declare function isVariable(str: any): boolean;
export declare function matchValue(str: any): RegExpExecArray;
export declare function parseLiteral(str: any): any;
-4
View File
@@ -1,4 +0,0 @@
import DelimitedToken from './delimited-token';
export default class OutputToken extends DelimitedToken {
constructor(raw: any, value: any, pos: any, input: any, file: any, line: any);
}
-16
View File
@@ -1,16 +0,0 @@
import Token from 'src/parser/token';
import ITemplate from 'src/template/itemplate';
declare type parseToken = (token: Token, remainTokens: Array<Token>) => ITemplate;
declare type eventHandler = ((arg?: Token | ITemplate) => void);
export default class ParseStream {
private tokens;
private handlers;
private stopRequested;
private parseToken;
constructor(tokens: Array<Token>, parseToken: parseToken);
on(name: string, cb: eventHandler): this;
trigger(event: string, arg?: Token | ITemplate): boolean;
start(): this;
stop(): this;
}
export {};
-13
View File
@@ -1,13 +0,0 @@
import Liquid from 'src/liquid';
import ParseStream from './parse-stream';
import Token from './token';
import Tag from 'src/template/tag/tag';
import Output from 'src/template/output';
import HTML from 'src/template/html';
export default class Parser {
liquid: Liquid;
constructor(liquid: Liquid);
parse(tokens: Array<Token>): any[];
parseToken(token: Token, remainTokens: Array<Token>): Tag | Output | HTML;
parseStream(tokens: Array<Token>): ParseStream;
}
-6
View File
@@ -1,6 +0,0 @@
import DelimitedToken from './delimited-token';
export default class TagToken extends DelimitedToken {
name: string;
args: string;
constructor(raw: any, value: any, pos: any, input: any, file: any, line: any);
}
-10
View File
@@ -1,10 +0,0 @@
export default class Token {
type: string;
line: number;
col: number;
raw: string;
input: string;
file: string;
value: string;
constructor(raw: any, col: any, input: any, file: any, line: any);
}
-6
View File
@@ -1,6 +0,0 @@
import { NormalizedFullOptions } from '../liquid-options';
export default class Tokenizer {
options: NormalizedFullOptions;
constructor(options?: NormalizedFullOptions);
tokenize(input: string, file?: string): any[];
}
-3
View File
@@ -1,3 +0,0 @@
import Token from 'src/parser/token';
import { NormalizedFullOptions } from 'src/liquid-options';
export default function whiteSpaceCtrl(tokens: Token[], options: NormalizedFullOptions): void;
-3
View File
@@ -1,3 +0,0 @@
export default class Render {
renderTemplates(templates: any, scope: any): Promise<string>;
}
-4
View File
@@ -1,4 +0,0 @@
export declare function evalExp(exp: any, scope: any): any;
export declare function evalValue(str: any, scope: any): any;
export declare function isTruthy(val: any): boolean;
export declare function isFalsy(val: any): boolean;
-5
View File
@@ -1,5 +0,0 @@
declare enum BlockMode {
OUTPUT = 0,
STORE = 1
}
export default BlockMode;
-18
View File
@@ -1,18 +0,0 @@
import { NormalizedFullOptions } from '../liquid-options';
import BlockMode from './block-mode';
export default class Scope {
opts: NormalizedFullOptions;
contexts: Array<object>;
blocks: object;
blockMode: BlockMode;
constructor(ctx?: object, opts?: NormalizedFullOptions);
getAll(): object;
get(path: string): any;
set(path: string, v: any): void;
unshift(ctx: object): number;
push(ctx: object): number;
pop(ctx?: object): object;
findContextFor(key: string, filter?: ((conttext: object) => boolean)): object;
readProperty(obj: any, key: any): any;
propertyAccessSeq(str: any): any[];
}
-8
View File
@@ -1,8 +0,0 @@
export declare class CaptureScope {
}
export declare class AssignScope {
}
export declare class IncrementScope {
}
export declare class DecrementScope {
}
-14
View File
@@ -1,14 +0,0 @@
import Scope from 'src/scope/scope';
declare type impl = (value: any, ...args: any[]) => any;
export default class Filter {
name: string;
impl: impl;
args: string[];
private static impls;
constructor(str: string, strictFilters?: boolean);
parseArgs(argList: string): string[];
render(value: any, scope: Scope): any;
static register(name: any, filter: any): void;
static clear(): void;
}
export {};
-8
View File
@@ -1,8 +0,0 @@
import Template from 'src/template/template';
import ITemplate from 'src/template/itemplate';
import Token from 'src/parser/token';
export default class extends Template implements ITemplate {
str: string;
constructor(token: Token);
render(): Promise<string>;
}
-6
View File
@@ -1,6 +0,0 @@
import Scope from 'src/scope/scope';
import Token from 'src/parser/token';
export default interface ITemplate {
token: Token;
render(scope: Scope): Promise<string>;
}
-10
View File
@@ -1,10 +0,0 @@
import Value from './value';
import Template from 'src/template/template';
import ITemplate from 'src/template/itemplate';
import Scope from 'src/scope/scope';
import OutputToken from 'src/parser/output-token';
export default class Output extends Template implements ITemplate {
value: Value;
constructor(token: OutputToken, strictFilters?: boolean);
render(scope: Scope): Promise<string>;
}
-10
View File
@@ -1,10 +0,0 @@
/**
* Key-Value Pairs Representing Tag Arguments
* Example:
* For the markup `{% include 'head.html' foo='bar' %}`,
* hash['foo'] === 'bar'
*/
export default class Hash {
[key: string]: any;
constructor(markup: any, scope: any);
}
-9
View File
@@ -1,9 +0,0 @@
import Scope from 'src/scope/scope';
import TagToken from 'src/parser/tag-token';
import Token from 'src/parser/token';
import Hash from 'src/template/tag/hash';
import ITagImpl from './itag-impl';
export default interface ITagImplOptions {
parse?: (this: ITagImpl, token: TagToken, remainingTokens: Array<Token>) => void;
render?: (this: ITagImpl, scope: Scope, hash: Hash) => any | Promise<any>;
}
-5
View File
@@ -1,5 +0,0 @@
import Liquid from 'src/liquid';
import ITagImplOptions from './itag-impl-options';
export default interface ITagImpl extends ITagImplOptions {
liquid: Liquid;
}
-19
View File
@@ -1,19 +0,0 @@
import Scope from 'src/scope/scope';
import ITagImplOptions from './itag-impl-options';
import Liquid from 'src/liquid';
import Template from 'src/template/template';
import ITemplate from 'src/template/itemplate';
import TagToken from 'src/parser/tag-token';
import Token from 'src/parser/token';
export default class Tag extends Template implements ITemplate {
name: string;
token: TagToken;
private impl;
static impls: {
[key: string]: ITagImplOptions;
};
constructor(token: TagToken, tokens: Token[], liquid: Liquid);
render(scope: Scope): Promise<string>;
static register(name: string, tag: ITagImplOptions): void;
static clear(): void;
}
-5
View File
@@ -1,5 +0,0 @@
import Token from 'src/parser/token';
export default class Template {
token: Token;
constructor(token: any);
}
-7
View File
@@ -1,7 +0,0 @@
import Scope from 'src/scope/scope';
export default class {
initial: any;
filters: Array<any>;
constructor(str: string, strictFilters?: boolean);
value(scope: Scope): any;
}
-2
View File
@@ -1,2 +0,0 @@
export { AssignScope, CaptureScope, IncrementScope, DecrementScope } from './scope/scopes';
export { ParseError, TokenizationError, RenderBreakError, AssertionError } from './util/error';
-1
View File
@@ -1 +0,0 @@
export default function (predicate: any, message?: string): void;
-30
View File
@@ -1,30 +0,0 @@
declare abstract class LiquidError {
name: string;
message: string;
stack: string;
private file;
private input;
private token;
private originalError;
constructor(err: any, token: any);
captureStackTrace(obj: any): void;
}
export declare class TokenizationError extends LiquidError {
constructor(message: any, token: any);
}
export declare class ParseError extends LiquidError {
constructor(err: any, token: any);
}
export declare class RenderError extends LiquidError {
constructor(err: any, tpl: any);
}
export declare class RenderBreakError {
message: string;
resolvedHTML: string;
constructor(message: any);
}
export declare class AssertionError {
message: string;
constructor(message: any);
}
export {};
-2
View File
@@ -1,2 +0,0 @@
export declare function anySeries(iterable: any, iteratee: any): Promise<any>;
export declare function mapSeries(iterable: any, iteratee: any): Promise<any[]>;
-1
View File
@@ -1 +0,0 @@
export default function (d: any, format: any): string;
-15
View File
@@ -1,15 +0,0 @@
export declare function isString(value: any): boolean;
export declare function isFunction(value: any): boolean;
export declare function promisify(fn: any): (...args: any[]) => Promise<{}>;
export declare function stringify(value: any): string;
export declare function create<T1 extends object, T2 extends T1 = T1>(proto: T1): T2;
export declare function isNil(value: any): boolean;
export declare function isArray(value: any): boolean;
export declare function isError(value: any): boolean;
export declare function forOwn(object: any, iteratee: ((val: any, key: string, obj: object) => boolean | void)): any;
export declare function assign(obj: object, ...srcs: object[]): object;
export declare function last(arr: any[]): any;
export declare function uniq(arr: any[]): any[];
export declare function isObject(value: any): boolean;
export declare function range(start: number, stop?: number, step?: number): any[];
export declare function padStart(str: any, length: number, ch?: string): any;
Vendored
+6
View File
@@ -0,0 +1,6 @@
declare module 'rollup-plugin-uglify' {
export function uglify (): any
}
declare module 'rollup-plugin-replace' {
export default function replace(options: any): any
}
+2944 -2574
View File
File diff suppressed because it is too large Load Diff
+31 -18
View File
@@ -1,19 +1,21 @@
{
"name": "liquidjs",
"version": "7.2.0",
"version": "8.4.1",
"description": "Liquid template engine by pure JavaScript: compatible to shopify, easy to extend.",
"main": "dist/liquid.common.js",
"types": "dist/liquid.d.ts",
"browser": "dist/liquid.js",
"scripts": {
"lint": "eslint . --ext .ts --ext .js",
"unit": "mocha 'test/unit/**/*.ts'",
"e2e": "npm run build && mocha 'test/e2e/**/*.ts'",
"test": "npm run unit && npm run e2e",
"coverage-html": "nyc npm run unit && nyc report --reporter=html",
"coverage-coveralls": "nyc npm run unit && nyc report --reporter=text-lcov | coveralls",
"build": "rollup -c && ls -lh dist",
"version": "npm run build && git add -A dist"
"lint": "eslint . --ext .ts",
"unit": "mocha \"test/unit/**/*.ts\"",
"integration": "mocha \"test/integration/**/*.ts\"",
"e2e": "npm run build && mocha \"test/e2e/**/*.ts\"",
"test": "cross-env BUNDLES=cjs,umd npm run build && mocha \"test/**/*.ts\"",
"benchmark": "ts-node benchmark",
"coverage-html": "nyc --reporter=html mocha \"test/{unit,integration}/**/*.ts\"",
"coverage-coveralls": "nyc mocha \"test/{unit,integration}/**/*.ts\" && nyc report --reporter=text-lcov | coveralls",
"build": "rollup -c rollup.config.ts && ls -lh dist",
"version": "npm run build"
},
"repository": {
"type": "git",
@@ -44,11 +46,20 @@
"@semantic-release/changelog": "^3.0.2",
"@semantic-release/commit-analyzer": "^6.1.0",
"@semantic-release/git": "^7.0.8",
"@semantic-release/npm": "^5.1.4",
"@semantic-release/npm": "^5.1.8",
"@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",
"@types/jsdom": "^12.2.2",
"@types/mocha": "^5.2.6",
"@typescript-eslint/eslint-plugin": "^1.3.0",
"@types/sinon": "^7.0.6",
"@types/sinon-chai": "^3.2.2",
"@types/supertest": "^2.0.7",
"@typescript-eslint/eslint-plugin": "^1.11.0",
"@typescript-eslint/parser": "^1.11.0",
"benchmark": "^2.1.4",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"coveralls": "^3.0.2",
@@ -56,7 +67,7 @@
"eslint": "^5.12.1",
"eslint-config-standard": "^12.0.0",
"eslint-plugin-import": "^2.15.0",
"eslint-plugin-mocha": "^5.2.1",
"eslint-plugin-mocha": "^5.3.0",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
@@ -66,15 +77,15 @@
"nyc": "^13.1.0",
"regenerator-runtime": "^0.12.1",
"rollup": "^1.1.2",
"rollup-plugin-shim": "^1.0.0",
"rollup-plugin-typescript2": "^0.19.2",
"rollup-plugin-replace": "^2.1.0",
"rollup-plugin-typescript2": "^0.21.1",
"rollup-plugin-uglify": "^6.0.2",
"semantic-release": "^15.13.3",
"semantic-release": "^15.13.14",
"sinon": "^7.2.3",
"sinon-chai": "^3.3.0",
"supertest": "^3.4.2",
"ts-node": "^8.0.2",
"tsconfig-paths": "^3.8.0",
"tslib": "^1.9.3",
"typescript": "^3.3.3"
},
"release": {
@@ -84,12 +95,14 @@
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/git"
"@semantic-release/git",
"@semantic-release/github"
]
},
"nyc": {
"extension": [
".ts"
]
}
},
"dependencies": {}
}
+40 -16
View File
@@ -1,9 +1,8 @@
import shim from 'rollup-plugin-shim'
import { uglify } from 'rollup-plugin-uglify'
import pkg from './package.json'
import typescript from 'rollup-plugin-typescript2'
import replace from 'rollup-plugin-replace'
const fake = { fs: `export default {}`, path: `export default {}` }
const version = process.env.VERSION || pkg.version
const sourcemap = true
const banner = `/*
@@ -14,9 +13,9 @@ const banner = `/*
const treeshake = {
propertyReadSideEffects: false
}
const input = 'src/liquid.ts'
const input = './src/liquid.ts'
export default [{
const cjs = {
output: [{
file: 'dist/liquid.common.js',
name: 'Liquid',
@@ -27,15 +26,19 @@ export default [{
external: ['path', 'fs'],
plugins: [typescript({
tsconfigOverride: {
exclude: [ 'test' ],
include: [ 'src' ],
exclude: [ 'test', 'benchmark' ],
compilerOptions: {
target: 'es5',
module: 'ES2015'
}
}
})],
treeshake,
input
}, {
}
const umd = {
output: [{
file: 'dist/liquid.js',
name: 'Liquid',
@@ -44,20 +47,27 @@ export default [{
banner
}],
plugins: [
shim(fake),
replace({
include: './src/liquid.ts',
delimiters: ['', ''],
'./fs/node': './fs/browser'
}),
typescript({
tsconfigOverride: {
exclude: [ 'test' ],
include: [ 'src' ],
exclude: [ 'test', 'benchmark' ],
compilerOptions: {
module: 'ES2015',
paths: { 'src/fs': ['src/fs/browser'] }
target: 'es5',
module: 'ES2015'
}
}
})
],
treeshake,
input
}, {
}
const min = {
output: [{
file: 'dist/liquid.min.js',
name: 'Liquid',
@@ -65,13 +75,18 @@ export default [{
sourcemap
}],
plugins: [
shim(fake),
replace({
include: './src/liquid.ts',
delimiters: ['', ''],
'./fs/node': './fs/browser'
}),
typescript({
tsconfigOverride: {
exclude: [ 'test' ],
include: [ 'src' ],
exclude: [ 'test', 'benchmark' ],
compilerOptions: {
module: 'ES2015',
paths: { 'src/fs': ['src/fs/browser'] }
target: 'es5',
module: 'ES2015'
}
}
}),
@@ -79,4 +94,13 @@ export default [{
],
treeshake,
input
}]
}
const bundles = []
const env = process.env.BUNDLES || ''
if (env.includes('cjs')) bundles.push(cjs)
if (env.includes('umd')) bundles.push(umd)
if (env.includes('min')) bundles.push(min)
if (bundles.length === 0) bundles.push(cjs, umd, min)
export default bundles
+39 -22
View File
@@ -1,25 +1,42 @@
import { last } from 'src/util/underscore'
import { isArray, last } from '../../util/underscore'
import { isTruthy } from '../../render/syntax'
export default {
'join': (v, arg) => v.join(arg === undefined ? ' ' : arg),
'last': v => last(v),
'first': v => v[0],
'map': (arr, arg) => arr.map(v => v[arg]),
'reverse': v => v.reverse(),
'sort': (v, arg) => v.sort(arg),
'size': v => v.length,
'slice': (v, begin, length) => {
if (length === undefined) length = 1
return v.slice(begin, begin + length)
},
'uniq': function (arr) {
const u = {}
return (arr || []).filter(val => {
if (u.hasOwnProperty(val)) {
return false
}
u[val] = true
return true
})
}
'join': (v: any[], arg: string) => v.join(arg === undefined ? ' ' : arg),
'last': (v: any) => isArray(v) ? last(v) : '',
'first': (v: any) => isArray(v) ? v[0] : '',
'map': map,
'reverse': (v: any[]) => [...v].reverse(),
'sort': <T>(v: T[], arg: (lhs: T, rhs: T) => number) => v.sort(arg),
'size': (v: string | any[]) => (v && v.length) || 0,
'concat': concat,
'slice': slice,
'uniq': uniq,
'where': where
}
function map<T1, T2> (arr: {[key: string]: T1}[], arg: string): T1[] {
return arr.map(v => v[arg])
}
function concat<T1, T2> (v: T1[], arg: T2[] | T2): (T1 | T2)[] {
return Array.prototype.concat.call(v, arg)
}
function slice<T> (v: T[], begin: number, length: number = 1): T[] {
begin = begin < 0 ? v.length + begin : begin
return v.slice(begin, begin + length)
}
function where<T> (arr: T[], property: string, value?: any): T[] {
return arr.filter(obj => value === undefined ? isTruthy(obj[property]) : obj[property] === value)
}
function uniq<T> (arr: T[]): T[] {
const u = {}
return (arr || []).filter(val => {
if (u.hasOwnProperty(String(val))) return false
u[String(val)] = true
return true
})
}
+7 -5
View File
@@ -1,18 +1,20 @@
import strftime from 'src/util/strftime'
import { isString } from 'src/util/underscore'
import strftime from '../../util/strftime'
import { isString, isNumber } from '../../util/underscore'
export default {
'date': (v, arg) => {
'date': (v: string | Date, arg: string) => {
let date = v
if (v === 'now') {
date = new Date()
} else if (isNumber(v)) {
date = new Date(v * 1000)
} else if (isString(v)) {
date = new Date(v)
date = /^\d+$/.test(v) ? new Date(+v * 1000) : new Date(v)
}
return isValidDate(date) ? strftime(date, arg) : v
}
}
function isValidDate (date) {
function isValidDate (date: any): date is Date {
return date instanceof Date && !isNaN(date.getTime())
}
+5 -5
View File
@@ -13,17 +13,17 @@ const unescapeMap = {
'&#39;': "'"
}
function escape (str) {
function escape (str: string) {
return String(str).replace(/&|<|>|"|'/g, m => escapeMap[m])
}
function unescape (str) {
function unescape (str: string) {
return String(str).replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m])
}
export default {
'escape': escape,
'escape_once': str => escape(unescape(str)),
'newline_to_br': v => v.replace(/\n/g, '<br />'),
'strip_html': v => String(v).replace(/<script.*?<\/script>|<!--.*?-->|<style.*?<\/style>|<.*?>/g, '')
'escape_once': (str: string) => escape(unescape(str)),
'newline_to_br': (v: string) => v.replace(/\n/g, '<br />'),
'strip_html': (v: string) => v.replace(/<script.*?<\/script>|<!--.*?-->|<style.*?<\/style>|<.*?>/g, '')
}
+30 -17
View File
@@ -1,26 +1,39 @@
const toLowerCase = String.prototype.toLowerCase
export default {
'abs': v => Math.abs(v),
'ceil': v => Math.ceil(v),
'divided_by': (v, arg) => v / arg,
'floor': v => Math.floor(v),
'minus': bindFixed((v, arg) => v - arg),
'modulo': bindFixed((v, arg) => v % arg),
'round': (v, arg) => {
const amp = Math.pow(10, arg || 0)
'abs': (v: number) => Math.abs(v),
'at_least': (v: number, n: number) => Math.max(v, n),
'at_most': (v: number, n: number) => Math.min(v, n),
'ceil': (v: number) => Math.ceil(v),
'divided_by': (v: number, arg: number) => v / arg,
'floor': (v: number) => Math.floor(v),
'minus': (v: number, arg: number) => v - arg,
'modulo': (v: number, arg: number) => v % arg,
'round': (v: number, arg: number = 0) => {
const amp = Math.pow(10, arg)
return Math.round(v * amp) / amp
},
'plus': bindFixed((v, arg) => Number(v) + Number(arg)),
'times': (v, arg) => v * arg
'plus': (v: number, arg: number) => Number(v) + Number(arg),
'sort_natural': sortNatural,
'times': (v: number, arg: number) => v * arg
}
function getFixed (v) {
const p = String(v).split('.')
return (p.length > 1) ? p[1].length : 0
function caseInsensitiveCmp (a: any, b: any) {
if (!b) return -1
if (!a) return 1
a = toLowerCase.call(a)
b = toLowerCase.call(b)
if (a < b) return -1
if (a > b) return 1
return 0
}
function bindFixed (cb) {
return (l, r) => {
const f = Math.max(getFixed(l), getFixed(r))
return cb(l, r).toFixed(f)
function sortNatural (input: any[], property?: string) {
if (!input || !input.sort) return []
if (property !== undefined) {
return [...input].sort(
(lhs, rhs) => caseInsensitiveCmp(lhs[property], rhs[property])
)
}
return [...input].sort(caseInsensitiveCmp)
}
+5 -2
View File
@@ -1,5 +1,8 @@
import { isTruthy } from 'src/render/syntax'
import { isFalsy } from '../../render/syntax'
import { toValue } from '../../util/underscore'
export default {
'default': (v, arg) => isTruthy(v) ? v : arg
'default': function<T1, T2> (v: string | T1, arg: T2): string | T1 | T2 {
return isFalsy(toValue(v)) || v === '' ? arg : v
}
}
+49 -30
View File
@@ -1,32 +1,51 @@
/**
* String related filters
*
* * prefer stringify() to String() since `undefined`, `null` should eval ''
*/
import { stringify } from '../../util/underscore'
export default {
'append': (v, arg) => v + arg,
'prepend': (v, arg) => arg + v,
'capitalize': str => String(str).charAt(0).toUpperCase() + str.slice(1),
'concat': (v, arg) => Array.prototype.concat.call(v, arg),
'lstrip': v => String(v).replace(/^\s+/, ''),
'downcase': v => v.toLowerCase(),
'upcase': str => String(str).toUpperCase(),
'remove': (v, arg) => v.split(arg).join(''),
'remove_first': (v, l) => v.replace(l, ''),
'replace': (v, pattern, replacement) =>
String(v).split(pattern).join(replacement),
'replace_first': (v, arg1, arg2) => String(v).replace(arg1, arg2),
'rstrip': str => String(str).replace(/\s+$/, ''),
'split': (v, arg) => String(v).split(arg),
'strip': (v) => String(v).trim(),
'strip_newlines': v => String(v).replace(/\n/g, ''),
'truncate': (v, l, o) => {
v = String(v)
o = (o === undefined) ? '...' : o
l = l || 16
if (v.length <= l) return v
return v.substr(0, l - o.length) + o
},
'truncatewords': (v, l, o) => {
if (o === undefined) o = '...'
const arr = v.split(' ')
let ret = arr.slice(0, l).join(' ')
if (arr.length > l) ret += o
return ret
}
'append': (v: string, arg: string) => stringify(v) + arg,
'prepend': (v: string, arg: string) => arg + stringify(v),
'capitalize': capitalize,
'lstrip': (v: string) => stringify(v).replace(/^\s+/, ''),
'downcase': (v: string) => stringify(v).toLowerCase(),
'upcase': (str: string) => stringify(str).toUpperCase(),
'remove': (v: string, arg: string) => stringify(v).split(arg).join(''),
'remove_first': (v: string, l: string) => stringify(v).replace(l, ''),
'replace': replace,
'replace_first': replaceFirst,
'rstrip': (str: string) => stringify(str).replace(/\s+$/, ''),
'split': (v: string, arg: string) => stringify(v).split(arg),
'strip': (v: string) => stringify(v).trim(),
'strip_newlines': (v: string) => stringify(v).replace(/\n/g, ''),
'truncate': truncate,
'truncatewords': truncateWords
}
function capitalize (str: string) {
str = stringify(str)
return str.charAt(0).toUpperCase() + str.slice(1)
}
function replace (v: string, pattern: string, replacement: string) {
return stringify(v).split(pattern).join(replacement)
}
function replaceFirst (v: string, arg1: string, arg2: string) {
return stringify(v).replace(arg1, arg2)
}
function truncate (v: string, l: number = 50, o: string = '...') {
v = stringify(v)
if (v.length <= l) return v
return v.substr(0, l - o.length) + o
}
function truncateWords (v: string, l: number = 15, o: string = '...') {
const arr = v.split(/\s+/)
let ret = arr.slice(0, l).join(' ')
if (arr.length >= l) ret += o
return ret
}
+2 -2
View File
@@ -1,4 +1,4 @@
export default {
'url_decode': x => x.split('+').map(decodeURIComponent).join(' '),
'url_encode': x => x.split(' ').map(encodeURIComponent).join('+')
'url_decode': (x: string) => x.split('+').map(decodeURIComponent).join(' '),
'url_encode': (x: string) => x.split(' ').map(encodeURIComponent).join('+')
}
+10 -11
View File
@@ -1,20 +1,19 @@
import assert from 'src/util/assert'
import { identifier } from 'src/parser/lexical'
import { AssignScope } from 'src/scope/scopes'
import assert from '../../util/assert'
import { identifier } from '../../parser/lexical'
import TagToken from '../../parser/tag-token'
import Context from '../../context/context'
import ITagImplOptions from '../../template/tag/itag-impl-options'
const re = new RegExp(`(${identifier.source})\\s*=([^]*)`)
export default {
parse: function (token) {
const match = token.args.match(re)
parse: function (token: TagToken) {
const match = token.args.match(re) as RegExpMatchArray
assert(match, `illegal token ${token.raw}`)
this.key = match[1]
this.value = match[2]
},
render: function (scope) {
const ctx = new AssignScope()
ctx[this.key] = this.liquid.evalValue(this.value, scope)
scope.push(ctx)
return Promise.resolve('')
render: async function (ctx: Context) {
ctx.front()[this.key] = await this.liquid.evalValue(this.value, ctx)
}
}
} as ITagImplOptions
+18 -12
View File
@@ -1,29 +1,35 @@
import BlockMode from 'src/scope/block-mode'
import BlockMode from '../../context/block-mode'
import TagToken from '../../parser/tag-token'
import Token from '../../parser/token'
import ITemplate from '../../template/itemplate'
import Context from '../../context/context'
import ITagImplOptions from '../../template/tag/itag-impl-options'
import ParseStream from '../../parser/parse-stream'
export default {
parse: function (token, remainTokens) {
parse: function (token: TagToken, remainTokens: Token[]) {
const match = /\w+/.exec(token.args)
this.block = match ? match[0] : ''
this.tpls = []
const stream = this.liquid.parser.parseStream(remainTokens)
this.tpls = [] as ITemplate[]
const stream: ParseStream = this.liquid.parser.parseStream(remainTokens)
.on('tag:endblock', () => stream.stop())
.on('template', tpl => this.tpls.push(tpl))
.on('template', (tpl: ITemplate) => this.tpls.push(tpl))
.on('end', () => {
throw new Error(`tag ${token.raw} not closed`)
})
stream.start()
},
render: async function (scope) {
const childDefined = scope.blocks[this.block]
render: async function (ctx: Context) {
const blocks = ctx.getRegister('blocks')
const childDefined = blocks[this.block]
const html = childDefined !== undefined
? childDefined
: await this.liquid.renderer.renderTemplates(this.tpls, scope)
: await this.liquid.renderer.renderTemplates(this.tpls, ctx)
if (scope.blockMode === BlockMode.STORE) {
scope.blocks[this.block] = html
if (ctx.getRegister('blockMode', BlockMode.OUTPUT) === BlockMode.STORE) {
blocks[this.block] = html
return ''
}
return html
}
}
} as ITagImplOptions
+1 -1
View File
@@ -1,4 +1,4 @@
import { RenderBreakError } from 'src/util/error'
import { RenderBreakError } from '../../util/error'
export default {
render: async function () {
+12 -14
View File
@@ -1,15 +1,15 @@
import assert from 'src/util/assert'
import { identifier } from 'src/parser/lexical'
import { CaptureScope } from 'src/scope/scopes'
import TagToken from 'src/parser/tag-token'
import Token from 'src/parser/token'
import Scope from 'src/scope/scope'
import assert from '../../util/assert'
import { identifier } from '../../parser/lexical'
import TagToken from '../../parser/tag-token'
import Token from '../../parser/token'
import Context from '../../context/context'
import ITagImplOptions from '../../template/tag/itag-impl-options'
const re = new RegExp(`(${identifier.source})`)
export default {
parse: function (tagToken: TagToken, remainTokens: Token[]) {
const match = tagToken.args.match(re)
const match = tagToken.args.match(re) as RegExpMatchArray
assert(match, `${tagToken.args} not valid identifier`)
this.variable = match[1]
@@ -17,16 +17,14 @@ export default {
const stream = this.liquid.parser.parseStream(remainTokens)
stream.on('tag:endcapture', () => stream.stop())
.on('template', tpl => this.templates.push(tpl))
.on('template', (tpl) => this.templates.push(tpl))
.on('end', () => {
throw new Error(`tag ${tagToken.raw} not closed`)
})
stream.start()
},
render: async function (scope: Scope) {
const html = await this.liquid.renderer.renderTemplates(this.templates, scope)
const ctx = new CaptureScope()
ctx[this.variable] = html
scope.push(ctx)
render: async function (ctx: Context) {
const html = await this.liquid.renderer.renderTemplates(this.templates, ctx)
ctx.front()[this.variable] = html
}
}
} as ITagImplOptions
+18 -12
View File
@@ -1,14 +1,20 @@
import { evalExp } from 'src/render/syntax'
import { evalExp } from '../../render/syntax'
import TagToken from '../../parser/tag-token'
import Token from '../../parser/token'
import Context from '../../context/context'
import ITemplate from '../../template/itemplate'
import ITagImplOptions from '../../template/tag/itag-impl-options'
import ParseStream from '../../parser/parse-stream'
export default {
parse: function (tagToken, remainTokens) {
parse: function (tagToken: TagToken, remainTokens: Token[]) {
this.cond = tagToken.args
this.cases = []
this.elseTemplates = []
let p = []
const stream = this.liquid.parser.parseStream(remainTokens)
.on('tag:when', token => {
let p: ITemplate[] = []
const stream: ParseStream = this.liquid.parser.parseStream(remainTokens)
.on('tag:when', (token: TagToken) => {
this.cases.push({
val: token.args,
templates: p = []
@@ -16,7 +22,7 @@ export default {
})
.on('tag:else', () => (p = this.elseTemplates))
.on('tag:endcase', () => stream.stop())
.on('template', tpl => p.push(tpl))
.on('template', (tpl: ITemplate) => p.push(tpl))
.on('end', () => {
throw new Error(`tag ${tagToken.raw} not closed`)
})
@@ -24,15 +30,15 @@ export default {
stream.start()
},
render: function (scope) {
render: async function (ctx: Context) {
for (let i = 0; i < this.cases.length; i++) {
const branch = this.cases[i]
const val = evalExp(branch.val, scope)
const cond = evalExp(this.cond, scope)
const val = await evalExp(branch.val, ctx)
const cond = await evalExp(this.cond, ctx)
if (val === cond) {
return this.liquid.renderer.renderTemplates(branch.templates, scope)
return this.liquid.renderer.renderTemplates(branch.templates, ctx)
}
}
return this.liquid.renderer.renderTemplates(this.elseTemplates, scope)
return this.liquid.renderer.renderTemplates(this.elseTemplates, ctx)
}
}
} as ITagImplOptions
+7 -3
View File
@@ -1,8 +1,12 @@
import TagToken from '../../parser/tag-token'
import Token from '../../parser/token'
import ITagImplOptions from '../../template/tag/itag-impl-options'
export default {
parse: function (tagToken, remainTokens) {
parse: function (tagToken: TagToken, remainTokens: Token[]) {
const stream = this.liquid.parser.parseStream(remainTokens)
stream
.on('token', token => {
.on('token', (token: TagToken) => {
if (token.name === 'endcomment') stream.stop()
})
.on('end', () => {
@@ -10,4 +14,4 @@ export default {
})
stream.start()
}
}
} as ITagImplOptions
+1 -1
View File
@@ -1,4 +1,4 @@
import { RenderBreakError } from 'src/util/error'
import { RenderBreakError } from '../../util/error'
export default {
render: async function () {
+13 -11
View File
@@ -1,13 +1,16 @@
import assert from 'src/util/assert'
import { value as rValue } from 'src/parser/lexical'
import { evalValue } from 'src/render/syntax'
import assert from '../../util/assert'
import { value as rValue } from '../../parser/lexical'
import { evalValue } from '../../render/syntax'
import TagToken from '../../parser/tag-token'
import Context from '../../context/context'
import ITagImplOptions from '../../template/tag/itag-impl-options'
const groupRE = new RegExp(`^(?:(${rValue.source})\\s*:\\s*)?(.*)$`)
const candidatesRE = new RegExp(rValue.source, 'g')
export default {
parse: function (tagToken) {
let match = groupRE.exec(tagToken.args)
parse: function (tagToken: TagToken) {
let match: RegExpExecArray | null = groupRE.exec(tagToken.args) as RegExpExecArray
assert(match, `illegal tag: ${tagToken.raw}`)
this.group = match[1] || ''
@@ -21,11 +24,10 @@ export default {
assert(this.candidates.length, `empty candidates: ${tagToken.raw}`)
},
render: function (scope) {
const group = evalValue(this.group, scope)
render: async function (ctx: Context) {
const group = await evalValue(this.group, ctx)
const fingerprint = `cycle:${group}:` + this.candidates.join(',')
const groups = scope.opts.groups = scope.opts.groups || {}
const groups = ctx.getRegister('cycle')
let idx = groups[fingerprint]
if (idx === undefined) {
@@ -36,6 +38,6 @@ export default {
idx = (idx + 1) % this.candidates.length
groups[fingerprint] = idx
return evalValue(candidate, scope)
return evalValue(candidate, ctx)
}
}
} as ITagImplOptions

Some files were not shown because too many files have changed in this diff Show More