mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 20:30:39 -07:00
feat: inline comment tag (#514)
* style: remove unnecessary intermediate constant * feat: add inline comment tag * refactor: use readIdentifier when reading tag names * docs: add inline comment tag
This commit is contained in:
@@ -78,6 +78,7 @@ filters:
|
|||||||
|
|
||||||
tags:
|
tags:
|
||||||
overview: overview.html
|
overview: overview.html
|
||||||
|
"# (inline comment)": inline_comment.html
|
||||||
assign: assign.html
|
assign: assign.html
|
||||||
capture: capture.html
|
capture: capture.html
|
||||||
case: case.html
|
case: case.html
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
title: "# (inline comment)"
|
||||||
|
---
|
||||||
|
|
||||||
|
{% since %}v9.38.0{% endsince %}
|
||||||
|
|
||||||
|
Add comments to a Liquid template using an inline tag. Text enclosed in an inline comment tag will not be printed.
|
||||||
|
|
||||||
|
Input
|
||||||
|
```liquid
|
||||||
|
Anything inside an inline comment tag will not be printed.
|
||||||
|
{% # this is an inline comment %}
|
||||||
|
But every line must start with a '#'.
|
||||||
|
{%
|
||||||
|
# this is a comment
|
||||||
|
# that spans multiple lines
|
||||||
|
%}
|
||||||
|
```
|
||||||
|
|
||||||
|
Output
|
||||||
|
```text
|
||||||
|
Anything inside an inline comment tag will not be printed.
|
||||||
|
But every line must start with a '#'.
|
||||||
|
```
|
||||||
|
|
||||||
|
Inline comments are useful inside <a href="./liquid.html">`liquid`</a> tags too.
|
||||||
|
|
||||||
|
```liquid
|
||||||
|
{% liquid
|
||||||
|
# required args
|
||||||
|
assign product = collection.products.first
|
||||||
|
|
||||||
|
# optional args
|
||||||
|
assign should_show_border = should_show_border | default: true
|
||||||
|
assign should_highlight = should_highlight | default: false
|
||||||
|
%}
|
||||||
|
```
|
||||||
|
|
||||||
|
But they don't work well for commenting out blocks of Liquid code. The <a href="./comment.html">`comment`</a> block tag is the better option when you need to temporarily stop other tags from being executed.
|
||||||
|
|
||||||
|
Input
|
||||||
|
```liquid
|
||||||
|
{%- # {% echo 'Welcome to LiquidJS!' %} -%}
|
||||||
|
{% comment %}{% echo 'Welcome to LiquidJS!' %}{% endcomment %}
|
||||||
|
```
|
||||||
|
|
||||||
|
Output
|
||||||
|
```text
|
||||||
|
-%}
|
||||||
|
```
|
||||||
@@ -18,10 +18,11 @@ import Break from './break'
|
|||||||
import Continue from './continue'
|
import Continue from './continue'
|
||||||
import echo from './echo'
|
import echo from './echo'
|
||||||
import liquid from './liquid'
|
import liquid from './liquid'
|
||||||
|
import inlineComment from './inline-comment'
|
||||||
import { TagImplOptions } from '../../template/tag/tag-impl-options'
|
import { TagImplOptions } from '../../template/tag/tag-impl-options'
|
||||||
|
|
||||||
const tags: { [key: string]: TagImplOptions } = {
|
const tags: { [key: string]: TagImplOptions } = {
|
||||||
assign, 'for': For, capture, 'case': Case, comment, include, render, decrement, increment, cycle, 'if': If, layout, block, raw, tablerow, unless, 'break': Break, 'continue': Continue, echo, liquid
|
assign, 'for': For, capture, 'case': Case, comment, include, render, decrement, increment, cycle, 'if': If, layout, block, raw, tablerow, unless, 'break': Break, 'continue': Continue, echo, liquid, '#': inlineComment
|
||||||
}
|
}
|
||||||
|
|
||||||
export default tags
|
export default tags
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { TagToken } from '../../tokens/tag-token'
|
||||||
|
import { TopLevelToken } from '../../tokens/toplevel-token'
|
||||||
|
import { TagImplOptions } from '../../template/tag/tag-impl-options'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
parse: function (tagToken: TagToken, remainTokens: TopLevelToken[]) {
|
||||||
|
if (tagToken.args.search(/\n\s*[^#\s]/g) !== -1) {
|
||||||
|
throw new Error('every line of an inline comment must start with a \'#\' character')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as TagImplOptions
|
||||||
@@ -207,8 +207,7 @@ export class Tokenizer {
|
|||||||
const begin = this.p
|
const begin = this.p
|
||||||
let end = this.N
|
let end = this.N
|
||||||
if (this.readToDelimiter('\n') !== -1) end = this.p
|
if (this.readToDelimiter('\n') !== -1) end = this.p
|
||||||
const token = new LiquidTagToken(input, begin, end, options, file)
|
return new LiquidTagToken(input, begin, end, options, file)
|
||||||
return token
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mkError (msg: string, begin: number) {
|
mkError (msg: string, begin: number) {
|
||||||
@@ -234,6 +233,13 @@ export class Tokenizer {
|
|||||||
return new IdentifierToken(this.input, begin, this.p, this.file)
|
return new IdentifierToken(this.input, begin, this.p, this.file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readTagName (): string {
|
||||||
|
this.skipBlank()
|
||||||
|
// Handle inline comment tags
|
||||||
|
if (this.input[this.p] === '#') return this.input.slice(this.p, ++this.p)
|
||||||
|
return this.readIdentifier().getText()
|
||||||
|
}
|
||||||
|
|
||||||
readHashes (jekyllStyle?: boolean) {
|
readHashes (jekyllStyle?: boolean) {
|
||||||
const hashes = []
|
const hashes = []
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export class LiquidTagToken extends DelimitedToken {
|
|||||||
this.args = ''
|
this.args = ''
|
||||||
} else {
|
} else {
|
||||||
const tokenizer = new Tokenizer(this.content, options.operatorsTrie)
|
const tokenizer = new Tokenizer(this.content, options.operatorsTrie)
|
||||||
this.name = tokenizer.readIdentifier().getText()
|
this.name = tokenizer.readTagName()
|
||||||
if (!this.name) throw new TokenizationError(`illegal liquid tag syntax`, this)
|
if (!this.name) throw new TokenizationError(`illegal liquid tag syntax`, this)
|
||||||
|
|
||||||
tokenizer.skipBlank()
|
tokenizer.skipBlank()
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class TagToken extends DelimitedToken {
|
|||||||
super(TokenKind.Tag, value, input, begin, end, trimTagLeft, trimTagRight, file)
|
super(TokenKind.Tag, value, input, begin, end, trimTagLeft, trimTagRight, file)
|
||||||
|
|
||||||
const tokenizer = new Tokenizer(this.content, options.operatorsTrie)
|
const tokenizer = new Tokenizer(this.content, options.operatorsTrie)
|
||||||
this.name = tokenizer.readIdentifier().getText()
|
this.name = tokenizer.readTagName()
|
||||||
if (!this.name) throw new TokenizationError(`illegal tag syntax`, this)
|
if (!this.name) throw new TokenizationError(`illegal tag syntax`, this)
|
||||||
|
|
||||||
tokenizer.skipBlank()
|
tokenizer.skipBlank()
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { Liquid } from '../../../../src/liquid'
|
||||||
|
import { expect, use } from 'chai'
|
||||||
|
import * as chaiAsPromised from 'chai-as-promised'
|
||||||
|
|
||||||
|
use(chaiAsPromised)
|
||||||
|
|
||||||
|
describe('tags/inline-comment', function () {
|
||||||
|
const liquid = new Liquid()
|
||||||
|
it('should ignore plain string', async function () {
|
||||||
|
const src = 'My name is {% # super %} Shopify.'
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
return expect(html).to.equal('My name is Shopify.')
|
||||||
|
})
|
||||||
|
it('should ignore output tokens', async function () {
|
||||||
|
const src = '{% #\n{{ foo}} \n %}'
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
return expect(html).to.equal('')
|
||||||
|
})
|
||||||
|
it('should support whitespace control', async function () {
|
||||||
|
const src = '{%- # some comment \n -%}\nfoo'
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
return expect(html).to.equal('foo')
|
||||||
|
})
|
||||||
|
it('should handle hash without trailing whitespace', async function () {
|
||||||
|
const src = '{% #some comment %}'
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
return expect(html).to.equal('')
|
||||||
|
})
|
||||||
|
it('should handle hash without leading whitespace', async function () {
|
||||||
|
const src = '{%#some comment %}'
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
return expect(html).to.equal('')
|
||||||
|
})
|
||||||
|
it('should handle empty comment', async function () {
|
||||||
|
const src = '{%#%}'
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
return expect(html).to.equal('')
|
||||||
|
})
|
||||||
|
it('should support multiple lines', async function () {
|
||||||
|
const src = [
|
||||||
|
'{%-',
|
||||||
|
' # spread inline comments',
|
||||||
|
' # over multiple lines',
|
||||||
|
'-%}'
|
||||||
|
].join('\n')
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
return expect(html).to.equal('')
|
||||||
|
})
|
||||||
|
it('should enforce leading hashes', async function () {
|
||||||
|
const src = [
|
||||||
|
'{%-',
|
||||||
|
' # spread inline comments',
|
||||||
|
' over multiple lines',
|
||||||
|
'-%}'
|
||||||
|
].join('\n')
|
||||||
|
return expect(liquid.parseAndRender(src))
|
||||||
|
.to.be.rejectedWith(/every line of an inline comment must start with a '#' character/)
|
||||||
|
})
|
||||||
|
describe('sync support', function () {
|
||||||
|
it('should ignore plain string', function () {
|
||||||
|
const src = 'My name is {% # super %} Shopify.'
|
||||||
|
const html = liquid.parseAndRenderSync(src)
|
||||||
|
return expect(html).to.equal('My name is Shopify.')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('liquid tag', function () {
|
||||||
|
it('should treat lines starting with a hash as a comment', async function () {
|
||||||
|
const src = [
|
||||||
|
'{% liquid ',
|
||||||
|
' # first comment line',
|
||||||
|
' # second comment line',
|
||||||
|
'',
|
||||||
|
' # another comment line',
|
||||||
|
' echo \'Hello \'',
|
||||||
|
'',
|
||||||
|
' # more comments',
|
||||||
|
' echo \'goodbye\'',
|
||||||
|
'-%}'
|
||||||
|
].join('\n')
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
return expect(html).to.equal('Hello goodbye')
|
||||||
|
})
|
||||||
|
it('should handle lots of hashes', async function () {
|
||||||
|
const src = [
|
||||||
|
'{% liquid',
|
||||||
|
' ##########################',
|
||||||
|
' # spread inline comments #',
|
||||||
|
' ##########################',
|
||||||
|
'-%}'
|
||||||
|
].join('\n')
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
return expect(html).to.equal('')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -525,4 +525,33 @@ describe('Tokenizer', function () {
|
|||||||
expect(() => tokenizer.readLiquidTagTokens()).to.throw(/illegal liquid tag syntax/)
|
expect(() => tokenizer.readLiquidTagTokens()).to.throw(/illegal liquid tag syntax/)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe('#read inline comment tags', () => {
|
||||||
|
it('should allow hash characters in tag names', () => {
|
||||||
|
const tokenizer = new Tokenizer('{% # some comment %}', trie)
|
||||||
|
const tokens = tokenizer.readTopLevelTokens()
|
||||||
|
expect(tokens.length).to.equal(1)
|
||||||
|
const tag = tokens[0] as TagToken
|
||||||
|
expect(tag).instanceOf(TagToken)
|
||||||
|
expect(tag.name).to.equal('#')
|
||||||
|
expect(tag.args).to.equal('some comment')
|
||||||
|
})
|
||||||
|
it('should handle leading whitespace', () => {
|
||||||
|
const tokenizer = new Tokenizer('{%\n # some comment %}', trie)
|
||||||
|
const tokens = tokenizer.readTopLevelTokens()
|
||||||
|
expect(tokens.length).to.equal(1)
|
||||||
|
const tag = tokens[0] as TagToken
|
||||||
|
expect(tag).instanceOf(TagToken)
|
||||||
|
expect(tag.name).to.equal('#')
|
||||||
|
expect(tag.args).to.equal('some comment')
|
||||||
|
})
|
||||||
|
it('should handle no trailing whitespace', () => {
|
||||||
|
const tokenizer = new Tokenizer('{%\n #some comment %}', trie)
|
||||||
|
const tokens = tokenizer.readTopLevelTokens()
|
||||||
|
expect(tokens.length).to.equal(1)
|
||||||
|
const tag = tokens[0] as TagToken
|
||||||
|
expect(tag).instanceOf(TagToken)
|
||||||
|
expect(tag.name).to.equal('#')
|
||||||
|
expect(tag.args).to.equal('some comment')
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user