Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion lib/esm/ejs.js
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,15 @@ Template.prototype = {
str = str.replace(/%/g, delim)
.replace(/</g, open)
.replace(/>/g, close);
// The `_%>` whitespace-slurping close is only a close tag when its leading
// `_` is not part of a preceding JS identifier. Guard it with a lookbehind
// so `<%= foo_%>` reads the expression `foo_` instead of splitting it into
// `foo` + `_%>` (the spaced form `<%= foo_ %>` already keeps `foo_`).
// Added after delimiter substitution so the lookbehind syntax is not
// rewritten by the `<`/`>` replacements above. `delim`/`close` are already
// regex-escaped, so the close substring is reused as-is.
let slurpClose = '_' + delim + close;
str = str.replace(slurpClose, '(?<![$\\w])' + slurpClose);
return new RegExp(str);
},

Expand Down Expand Up @@ -735,9 +744,12 @@ Template.prototype = {
let closeWhitespaceSlurpTag = utils.escapeRegExpChars('_' + d + c);
let openWhitespaceSlurpReplacement = o + d + '_';
let closeWhitespaceSlurpReplacement = '_' + d + c;
// The close-tag pass is guarded by a lookbehind so a trailing identifier
// underscore (e.g. `<%= foo_%>`) is not mistaken for a `_%>` slurp close,
// which keeps its surrounding whitespace just like the spaced `<%= foo_ %>`.
this.templateText =
this.templateText.replace(new RegExp('[ \\t]*' + openWhitespaceSlurpTag, 'gm'), openWhitespaceSlurpReplacement)
.replace(new RegExp(closeWhitespaceSlurpTag + '[ \\t]*', 'gm'), closeWhitespaceSlurpReplacement);
.replace(new RegExp('(?<![$\\w])' + closeWhitespaceSlurpTag + '[ \\t]*', 'gm'), closeWhitespaceSlurpReplacement);

let matches = this.parseTemplateText();

Expand Down
12 changes: 12 additions & 0 deletions test/ejs.js
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,18 @@ suite('<%_ and _%>', function () {
assert.equal(ejs.render(fixture('space-and-tab-slurp.ejs'), {users: users}),
fixture('space-and-tab-slurp.html'));
});

test('does not truncate a trailing underscore in an expression', function () {
var data = {foo: 'WRONG', foo_: 'RIGHT'};
// `foo_%>` must read `foo_` and keep its whitespace, like `foo_ %>` does.
assert.equal(ejs.render('<%= foo_ %> END', data), 'RIGHT END');
assert.equal(ejs.render('<%= foo_%> END', data), 'RIGHT END');
});

test('still treats a real `_%>` as a whitespace-slurping close', function () {
assert.equal(ejs.render('<p> <%_ var x = 1; _%> \n<%= x %></p>'), '<p>1</p>');
assert.equal(ejs.render('<%= 1 _%> END'), '1END');
});
});

suite('single quotes', function () {
Expand Down