Skip to content

Commit b5d563d

Browse files
committed
1.1.3
- Added `<!--# escape comments -->`. - Updated dependencies.
1 parent 1315784 commit b5d563d

8 files changed

Lines changed: 46 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
- Put your changes here...
66

7+
## 1.1.3
8+
9+
- Added `<!--# escape comments -->`.
10+
- Updated dependencies.
11+
712
## 1.1.2
813

914
- Added `teddy.clearTemplates()` method to clear the template cache.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Display a variable by simply writing `{varName}` anywhere in the template. You c
9191

9292
All variable names are case-insensitive, both in `{varName}` form and when referenced in Teddy tags described below. This is to comply with the rules of HTML grammar which mandate that HTML tags and attributes be case insensitive.
9393

94-
HTML entities such as `<`, `>`, `&`, `'`, and `"` will be escaped by default as a safeguard against [cross-site scripting](https://en.wikipedia.org/wiki/Cross-site_scripting). If you want to deliberately escape some text or some HTML, then wrap it in an `<escape>` tag like this: `<escape><p>hello</p></escape>` which will output: `&lt;p&gt;hello&lt;/p&gt;`.
94+
HTML entities such as `<`, `>`, `&`, `'`, and `"` will be escaped by default as a safeguard against [cross-site scripting](https://en.wikipedia.org/wiki/Cross-site_scripting). If you want to deliberately escape some text or some HTML, then wrap it in an `<escape>` tag like `<escape><p>hello</p></escape>` or an escape comment like `<!--#<p>hello</p>-->` which will output: `&lt;p&gt;hello&lt;/p&gt;`.
9595

9696
If you need to suppress this escaping in certain scenarios, write your variable like this: `{varName|s}`.
9797

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"url": "https://github.com/rooseveltframework/teddy/graphs/contributors"
99
}
1010
],
11-
"version": "1.1.2",
11+
"version": "1.1.3",
1212
"files": [
1313
"dist"
1414
],

teddy.js

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ function loadTemplate (template) {
7171
}
7272
}
7373

74-
// remove teddy {! comments !} and <!--! comments -->
74+
// remove teddy {! comments !} and <!--! comments -->; also replace <!--# content --> with <escape>content</escape>
7575
function removeTeddyComments (renderedTemplate) {
7676
let oldTemplate
7777
do {
@@ -91,6 +91,13 @@ function removeTeddyComments (renderedTemplate) {
9191
return renderedTemplate
9292
}
9393
for (let i = 0; i < vars.length; i++) renderedTemplate = renderedTemplate.replace(`<!--!${vars[i]}-->`, '')
94+
95+
try {
96+
vars = matchByDelimiter(renderedTemplate, '<!--#', '-->')
97+
} catch (e) {
98+
return renderedTemplate
99+
}
100+
for (let i = 0; i < vars.length; i++) renderedTemplate = renderedTemplate.replace(`<!--#${vars[i]}-->`, `<escape>${vars[i]}</escape>`)
94101
} while (oldTemplate !== renderedTemplate)
95102
return renderedTemplate
96103
}
@@ -222,16 +229,16 @@ function parseIncludes (dom, model, dynamic) {
222229
const hasFalse = contents.includes(' false=')
223230
const hasLoop = contents.includes('</loop>')
224231
const hasInline = contents.includes('</inline>')
225-
const hasEscape = contents.includes('</escape>')
232+
const hasEscape = contents.includes('</escape>') || contents.includes('<!--#')
226233
const hasSelected = contents.includes(' selected-value=') || contents.includes(' checked-value=')
234+
if (hasEscape) contents = parseEscapes(contents)
227235
let localDom
228236
if (hasNoteddy || hasNoparse || hasPre) {
229237
localDom = cheerioLoad(contents, cheerioOptions)
230238
localDom = tagNoParseBlocks(localDom, localModel)
231239
contents = localDom.html()
232240
}
233241
localDom = cheerioLoad(parseVars(contents, localModel), cheerioOptions)
234-
if (hasEscape) localDom = parseEscapes(localDom, localModel)
235242
if (hasIf || hasUnless) localDom = parseConditionals(localDom, localModel)
236243
if (hasTrue || hasFalse) localDom = parseOneLineConditionals(localDom, localModel)
237244
if (hasLoop) localDom = parseLoops(localDom, localModel)
@@ -631,6 +638,8 @@ function parseLoops (dom, model) {
631638
const hasNoteddyLoopContents = loopContents.includes('</noteddy>')
632639
const hasNoparseLoopContents = loopContents.includes('</noparse>')
633640
const hasPreLoopContents = loopContents.includes('</pre>')
641+
const hasEscape = loopContents.includes('</escape>') || loopContents.includes('<!--#')
642+
if (hasEscape) loopContents = parseEscapes(loopContents)
634643
if (hasNoteddyLoopContents || hasNoparseLoopContents || hasPreLoopContents) {
635644
let localDom = cheerioLoad(loopContents, cheerioOptions)
636645
localDom = tagNoParseBlocks(localDom, localModel)
@@ -645,10 +654,8 @@ function parseLoops (dom, model) {
645654
const hasFalse = localMarkup.includes(' false=')
646655
const hasLoop = localMarkup.includes('</loop>')
647656
const hasInline = localMarkup.includes('</inline>')
648-
const hasEscape = localMarkup.includes('</escape>')
649657
const hasSelected = localMarkup.includes(' selected-value=') || localMarkup.includes(' checked-value=')
650658
let localDom = cheerioLoad(localMarkup || '', cheerioOptions)
651-
if (hasEscape) localDom = parseEscapes(localDom, localModel)
652659
if (hasNoteddy || hasNoparse) localDom = tagNoParseBlocks(localDom, localModel)
653660
if (hasIf || hasUnless) localDom = parseConditionals(localDom, localModel)
654661
if (hasTrue || hasFalse) localDom = parseOneLineConditionals(localDom, localModel)
@@ -700,19 +707,8 @@ function parseInlines (dom, model) {
700707
}
701708

702709
// render <escape> tags
703-
function parseEscapes (dom, model) {
704-
let parsedTags
705-
do {
706-
parsedTags = 0
707-
const tags = dom('escape')
708-
if (tags.length > 0) {
709-
for (const el of tags) {
710-
dom(el).replaceWith(escapeEntities(dom(el).html()))
711-
parsedTags++
712-
}
713-
}
714-
} while (parsedTags)
715-
return dom
710+
function parseEscapes (templateString) {
711+
return templateString.replace(/<escape>(.*?)<\/escape>/gs, (_, content) => escapeEntities(content.trim()))
716712
}
717713

718714
// render `selected-value` and `checked-value` attributes
@@ -1240,6 +1236,9 @@ function render (template, model, callback) {
12401236
})
12411237
}
12421238

1239+
const hasEscape = renderedTemplate.includes('</escape>') || renderedTemplate.includes('<!--#')
1240+
if (hasEscape) renderedTemplate = parseEscapes(renderedTemplate)
1241+
12431242
dom = cheerioLoad(renderedTemplate || '', cheerioOptions)
12441243
let oldTemplate
12451244
let passes = 0
@@ -1261,15 +1260,13 @@ function render (template, model, callback) {
12611260
const hasInclude = renderedTemplate.includes('</include>')
12621261
const hasLoop = renderedTemplate.includes('</loop>')
12631262
const hasInline = renderedTemplate.includes('</inline>')
1264-
const hasEscape = renderedTemplate.includes('</escape>')
12651263
const hasSelected = renderedTemplate.includes(' selected-value=') || renderedTemplate.includes(' checked-value=')
12661264
oldTemplate = renderedTemplate || ''
12671265
if (passes > 1) {
12681266
dom = cheerioLoad(renderedTemplate || '', cheerioOptions)
12691267
if (parseDynamicIncludes) dom = parseIncludes(dom, model, true)
12701268
}
12711269
if (hasCache) dom = replaceCacheElements(dom, model)
1272-
if (hasEscape) dom = parseEscapes(dom, model)
12731270
if (hasNoteddy || hasNoparse || hasPre) dom = tagNoParseBlocks(dom, model)
12741271
if (hasIf || hasUnless) dom = parseConditionals(dom, model)
12751272
if (hasTrue || hasFalse) dom = parseOneLineConditionals(dom, model)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{!
2+
should escape HTML entities present in <!--# escape --> tags
3+
!}
4+
5+
<div>
6+
<code><!--#<p>--></code>
7+
<code><!--#<script>--></code>
8+
</div>

test/templates/misc/serverSideCommentsWithTeddyCode.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
should not render this loop
1414
<loop through='objects' key='k' val='v'>
1515
-->
16+
<div>
17+
<!--#
18+
<p>hello</p>
19+
-->
20+
</div>
1621
<p>test</p>
1722
</loop>
1823
</div>

test/tests.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,12 @@ export default [
894894
run: async (teddy, template, model, assert, expected) => assert(teddy.render(template, model), expected),
895895
expected: '<div>&lt;p&gt;hello&lt;/p&gt;</div>'
896896
},
897+
{
898+
message: 'should escape HTML entities present in <!--# escape --> comments (misc/escapeComment.html)',
899+
template: 'misc/escapeComment',
900+
run: async (teddy, template, model, assert, expected) => assert(teddy.render(template, model), expected),
901+
expected: '<div><code>&lt;p&gt;</code><code>&lt;script&gt;</code></div>'
902+
},
897903
{
898904
message: 'should render <pre> tags correctly (misc/preTag.html)',
899905
template: 'misc/preTag',
@@ -1674,7 +1680,7 @@ export default [
16741680
message: 'should not render Teddy code in server-side comments in loops (misc/serverSideCommentsWithTeddyCode.html)',
16751681
template: 'misc/serverSideCommentsWithTeddyCode',
16761682
run: async (teddy, template, model, assert, expected) => assert(teddy.render(template, model), expected),
1677-
expected: '<div><p>test</p><p>test</p><p>test</p><p>test</p><p>test</p><p>test</p></div>'
1683+
expected: '<div><p>test</p><div>&lt;p&gt;hello&lt;/p&gt;</div><p>test</p><p>test</p><div>&lt;p&gt;hello&lt;/p&gt;</div><p>test</p><p>test</p><div>&lt;p&gt;hello&lt;/p&gt;</div><p>test</p></div>'
16781684
},
16791685
{
16801686
message: 'should render img tags correctly (misc/imgSrc.html)',

0 commit comments

Comments
 (0)