Skip to content
Closed
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
25 changes: 22 additions & 3 deletions packages/spacebars-compiler/spacebars_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,29 @@ Tinytest.add("spacebars-compiler - parse", function (test) {
test.equal(BlazeTools.toJS(SpacebarsCompiler.parse('{{#foo}}x{{else bar}}{{#baz}}z{{/baz}}{{/foo}}')),
'SpacebarsCompiler.TemplateTag({type: "BLOCKOPEN", path: ["foo"], content: "x", elseContent: SpacebarsCompiler.TemplateTag({type: "BLOCKOPEN", path: ["bar"], content: SpacebarsCompiler.TemplateTag({type: "BLOCKOPEN", path: ["baz"], content: "z"})})})');

// Triple-stache in start tag — should mention raw HTML
test.throws(function () {
SpacebarsCompiler.parse('<a {{{x}}}></a>');
});
}, 'not allowed in an HTML start tag');

// Block helper in start tag — should suggest attribute="{{helper}}" pattern
test.throws(function () {
SpacebarsCompiler.parse('<a {{#if x}}{{/if}}></a>');
});
}, 'not allowed in an HTML start tag');

// Common real-world patterns that trigger this error
test.throws(function () {
SpacebarsCompiler.parse('<input {{#if isDisabled}}disabled{{/if}}>');
}, 'Bad syntax');
test.throws(function () {
SpacebarsCompiler.parse('<option {{#unless isActive}}selected{{/unless}}>');
}, 'not allowed in an HTML start tag');

// Custom block helper in start tag
test.throws(function () {
SpacebarsCompiler.parse('<a {{#myHelper}}class="foo"{{/myHelper}}></a>');
}, 'not allowed in an HTML start tag');

test.throws(function () {
SpacebarsCompiler.parse('<a {{k}}={[v}}></a>');
});
Expand All @@ -261,9 +278,11 @@ Tinytest.add("spacebars-compiler - parse", function (test) {
test.throws(function () {
SpacebarsCompiler.parse('<a x{{y}}=z></a>');
});

// Template inclusion in start tag — should mention it's not allowed
test.throws(function () {
SpacebarsCompiler.parse('<a {{> x}}></a>');
});
}, 'template inclusions are not allowed');

test.equal(BlazeTools.toJS(SpacebarsCompiler.parse('<a {{! x--}} b=c{{! x}} {{! x}}></a>')),
'HTML.A({b: "c"})');
Expand Down
31 changes: 29 additions & 2 deletions packages/spacebars-compiler/templatetag.js
Original file line number Diff line number Diff line change
Expand Up @@ -517,8 +517,35 @@ const validateTag = function (ttag, scanner) {
scanner.fatal(`${ttag.type} template tag is not allowed in an HTML attribute`);
}
} else if (position === TEMPLATE_TAG_POSITION.IN_START_TAG) {
if (! (ttag.type === 'DOUBLE')) {
scanner.fatal(`Reactive HTML attributes must either have a constant name or consist of a single {{helper}} providing a dictionary of names and values. A template tag of type ${ttag.type} is not allowed here.`);
if (ttag.type === 'BLOCKOPEN') {
const path0 = ttag.path[0];
const isBuiltIn = ttag.path.length === 1 && (path0 === 'if' || path0 === 'unless' || path0 === 'with' || path0 === 'each');
if (isBuiltIn) {
scanner.fatal(
`{{#${path0}}} is not allowed in an HTML start tag. ` +
'To conditionally add a boolean attribute like "disabled", "checked", or "selected", ' +
'use the attribute="{{helper}}" syntax instead.\n' +
' Bad syntax: <input {{#if isDisabled}}disabled{{/if}}>\n' +
' Good syntax: <input disabled="{{isDisabled}}">'
);
} else {
scanner.fatal(
`{{#${path0}}} is not allowed in an HTML start tag. ` +
'Only a single {{helper}} returning a dictionary of attribute name=value pairs is allowed here.'
);
}
} else if (ttag.type === 'TRIPLE') {
scanner.fatal(
'Triple-stache {{{...}}} is not allowed in an HTML start tag because it outputs raw HTML. ' +
'Use a double-stache {{helper}} that returns a dictionary of attribute name=value pairs instead.'
);
} else if (ttag.type === 'INCLUSION') {
scanner.fatal(
`{{> ...}} template inclusions are not allowed in an HTML start tag. ` +
'Only a single {{helper}} returning a dictionary of attribute name=value pairs is allowed here.'
);
} else if (ttag.type !== 'DOUBLE') {
scanner.fatal(`A template tag of type "${ttag.type}" is not allowed in an HTML start tag.`);
}
if (scanner.peek() === '=') {
scanner.fatal("Template tags are not allowed in attribute names, only in attribute values or in the form of a single {{helper}} that evaluates to a dictionary of name=value pairs.");
Expand Down