Repository: harttle/liquidjs
Pull request #640 (2023-08-16 20:30:44)

fix: for throws undefined var with a null value with strictVariables.
Please advise if this ain't the right way to fix this. I _think_ I didn't break anything and this may catch other cases of the same problem.

Fixes: [639](https://github.com/harttle/liquidjs/issues/639)

ebobby: @harttle Changed as requested.

Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 8ca4cde1e..2b9f74620 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -631,6 +631,15 @@
       "contributions": [
         "code"
       ]
+    },
+    {
+      "login": "davidlj95",
+      "name": "David LJ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/8050648?v=4",
+      "profile": "https://www.davidlj95.com",
+      "contributions": [
+        "doc"
+      ]
     }
   ],
   "contributorsPerLine": 7,
diff --git a/README.md b/README.md
index 174f87071..30a07c063 100644
--- a/README.md
+++ b/README.md
@@ -175,6 +175,7 @@ Want to contribute? see [Contribution Guidelines][contribution]. Thanks goes to
       <td align="center" valign="top" width="14.28%"><a href="https://www.11ty.dev/"><img src="https://avatars.githubusercontent.com/u/35147177?v=4?s=100" width="100px;" alt="Eleventy"/><br /><sub><b>Eleventy</b></sub></a><br /><a href="#financial-11ty" title="Financial">💵</a></td>
       <td align="center" valign="top" width="14.28%"><a href="http://nickreilingh.com/"><img src="https://avatars.githubusercontent.com/u/2458645?v=4?s=100" width="100px;" alt="Nick Reilingh"/><br /><sub><b>Nick Reilingh</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=NReilingh" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="14.28%"><a href="http://ebobby.org"><img src="https://avatars.githubusercontent.com/u/170356?v=4?s=100" width="100px;" alt="Francisco Soto"/><br /><sub><b>Francisco Soto</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=ebobby" title="Code">💻</a></td>
+      <td align="center" valign="top" width="14.28%"><a href="https://www.davidlj95.com"><img src="https://avatars.githubusercontent.com/u/8050648?v=4?s=100" width="100px;" alt="David LJ"/><br /><sub><b>David LJ</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=davidlj95" title="Documentation">📖</a></td>
     </tr>
   </tbody>
 </table>
diff --git a/docs/source/tutorials/setup.md b/docs/source/tutorials/setup.md
index 8969628a2..d92a9b3cc 100644
--- a/docs/source/tutorials/setup.md
+++ b/docs/source/tutorials/setup.md
@@ -100,4 +100,4 @@ Most of the [options available through the JavaScript API][options] are also ava
 A ReactJS demo is also added by [@stevenanthonyrevo](https://github.com/stevenanthonyrevo), see [liquidjs/demo/reactjs/](https://github.com/harttle/liquidjs/blob/master/demo/reactjs/).
 
 [intro]: ./intro-to-liquid.html
-[options]: ./options.md
+[options]: ./options.html
diff --git a/src/context/context.ts b/src/context/context.ts
index a5dbe200f..64ab56f44 100644
--- a/src/context/context.ts
+++ b/src/context/context.ts
@@ -2,7 +2,7 @@ import { Drop } from '../drop/drop'
 import { __assign } from 'tslib'
 import { NormalizedFullOptions, defaultOptions, RenderOptions } from '../liquid-options'
 import { Scope } from './scope'
-import { isArray, isNil, isString, isFunction, toLiquid, InternalUndefinedVariableError, toValueSync } from '../util'
+import { isArray, isNil, isUndefined, isString, isFunction, toLiquid, InternalUndefinedVariableError, toValueSync } from '../util'
 
 type PropertyKey = string | number;
 
@@ -80,7 +80,7 @@ export class Context {
     if (isString(paths)) paths = paths.split('.')
     for (let i = 0; i < paths.length; i++) {
       scope = yield readProperty(scope as object, paths[i], this.ownPropertyOnly)
-      if (isNil(scope) && this.strictVariables) {
+      if (this.strictVariables && isUndefined(scope)) {
         throw new InternalUndefinedVariableError((paths as string[]).slice(0, i + 1).join!('.'))
       }
     }
diff --git a/src/tags/case.ts b/src/tags/case.ts
index b55df2465..23564d3a8 100644
--- a/src/tags/case.ts
+++ b/src/tags/case.ts
@@ -17,7 +17,12 @@ export default class extends Tag {
         const values: ValueToken[] = []
         while (!token.tokenizer.end()) {
           values.push(token.tokenizer.readValueOrThrow())
-          token.tokenizer.readTo(',')
+          token.tokenizer.skipBlank()
+          if (token.tokenizer.peek() === ',') {
+            token.tokenizer.readTo(',')
+          } else {
+            token.tokenizer.readTo('or')
+          }
         }
         this.branches.push({
           values,
diff --git a/src/util/underscore.ts b/src/util/underscore.ts
index fbe4e4983..08168df7b 100644
--- a/src/util/underscore.ts
+++ b/src/util/underscore.ts
@@ -63,6 +63,10 @@ export function isNil (value: any): boolean {
   return value == null
 }
 
+export function isUndefined (value: any): boolean {
+  return value === undefined
+}
+
 export function isArray (value: any): value is any[] {
   // be compatible with IE 8
   return toString.call(value) === '[object Array]'


Test patch:
diff --git a/test/integration/tags/case.spec.ts b/test/integration/tags/case.spec.ts
index d15d452cb..3312eb800 100644
--- a/test/integration/tags/case.spec.ts
+++ b/test/integration/tags/case.spec.ts
@@ -75,4 +75,20 @@ describe('tags/case', function () {
     const html = await liquid.parseAndRender(src)
     return expect(html).toBe('firstsecond')
   })
+  it('should support case with multiple values separated by or', async function () {
+    const src = '{% case 3 %}' +
+            '{% when 1 or 2 or 3 %}1 or 2 or 3' +
+            '{% else %}not 1 or 2 or 3' +
+            '{%endcase%}'
+    const html = await liquid.parseAndRender(src)
+    return expect(html).toBe('1 or 2 or 3')
+  })
+  it('should support case with multiple strings separated by or', async function () {
+    const src = '{% case "or" %}' +
+            '{% when "and" or "or" %}and or or' +
+            '{% else %}not and or or' +
+            '{%endcase%}'
+    const html = await liquid.parseAndRender(src)
+    return expect(html).toBe('and or or')
+  })
 })
diff --git a/test/integration/tags/for.spec.ts b/test/integration/tags/for.spec.ts
index 35c8e52d0..0d09b8c1a 100644
--- a/test/integration/tags/for.spec.ts
+++ b/test/integration/tags/for.spec.ts
@@ -59,6 +59,12 @@ describe('tags/for', function () {
     const html = await liquid.parseAndRender(src, scope)
     return expect(html).toBe('str-"string"-string')
   })
+  it('should not report undefined variable on null value', async function () {
+    const engine = new Liquid({ strictVariables: true })
+    const src = '{% assign hello = "hello,world" | split: "," | concat: null %}{% for i in hello %}{{ i }},{% endfor %}'
+    const html = await engine.parseAndRender(src, scope)
+    return expect(html).toBe('hello,world,,')
+  })
   describe('illegal', function () {
     it('should reject when for not closed', function () {
       const src = '{%for c in alpha%}{{c}}'