Convert src/compiler/parse/state/mustache.ts to JavaScript

pull/8569/head
Simon Holthausen 3 years ago
parent 2c541f66c1
commit 73f3fbc954

@ -4,128 +4,114 @@ import { closing_tag_omitted } from '../utils/html';
import { regex_whitespace } from '../../utils/patterns'; import { regex_whitespace } from '../../utils/patterns';
import { trim_start, trim_end } from '../../utils/trim'; import { trim_start, trim_end } from '../../utils/trim';
import { to_string } from '../utils/node'; import { to_string } from '../utils/node';
import { Parser } from '../index';
import { TemplateNode } from '../../interfaces';
import parser_errors from '../errors'; import parser_errors from '../errors';
function trim_whitespace(block: TemplateNode, trim_before: boolean, trim_after: boolean) { /**
if (!block.children || block.children.length === 0) return; // AwaitBlock * @param {TemplateNode} block
* @param {boolean} trim_before
* @param {boolean} trim_after
*/
function trim_whitespace(block, trim_before, trim_after) {
if (!block.children || block.children.length === 0)
return; // AwaitBlock
const first_child = block.children[0]; const first_child = block.children[0];
const last_child = block.children[block.children.length - 1]; const last_child = block.children[block.children.length - 1];
if (first_child.type === 'Text' && trim_before) { if (first_child.type === 'Text' && trim_before) {
first_child.data = trim_start(first_child.data); first_child.data = trim_start(first_child.data);
if (!first_child.data) block.children.shift(); if (!first_child.data)
block.children.shift();
} }
if (last_child.type === 'Text' && trim_after) { if (last_child.type === 'Text' && trim_after) {
last_child.data = trim_end(last_child.data); last_child.data = trim_end(last_child.data);
if (!last_child.data) block.children.pop(); if (!last_child.data)
block.children.pop();
} }
if (block.else) { if (block.else) {
trim_whitespace(block.else, trim_before, trim_after); trim_whitespace(block.else, trim_before, trim_after);
} }
if (first_child.elseif) { if (first_child.elseif) {
trim_whitespace(first_child, trim_before, trim_after); trim_whitespace(first_child, trim_before, trim_after);
} }
} }
const regex_whitespace_with_closing_curly_brace = /^\s*}/; const regex_whitespace_with_closing_curly_brace = /^\s*}/;
export default function mustache(parser: Parser) { /**
* @param {Parser} parser
*/
export default function mustache(parser) {
const start = parser.index; const start = parser.index;
parser.index += 1; parser.index += 1;
parser.allow_whitespace(); parser.allow_whitespace();
// {/if}, {/each}, {/await} or {/key} // {/if}, {/each}, {/await} or {/key}
if (parser.eat('/')) { if (parser.eat('/')) {
let block = parser.current(); let block = parser.current();
let expected; let expected;
if (closing_tag_omitted(block.name)) { if (closing_tag_omitted(block.name)) {
block.end = start; block.end = start;
parser.stack.pop(); parser.stack.pop();
block = parser.current(); block = parser.current();
} }
if (block.type === 'ElseBlock' ||
if (
block.type === 'ElseBlock' ||
block.type === 'PendingBlock' || block.type === 'PendingBlock' ||
block.type === 'ThenBlock' || block.type === 'ThenBlock' ||
block.type === 'CatchBlock' block.type === 'CatchBlock') {
) {
block.end = start; block.end = start;
parser.stack.pop(); parser.stack.pop();
block = parser.current(); block = parser.current();
expected = 'await'; expected = 'await';
} }
if (block.type === 'IfBlock') { if (block.type === 'IfBlock') {
expected = 'if'; expected = 'if';
} else if (block.type === 'EachBlock') { }
else if (block.type === 'EachBlock') {
expected = 'each'; expected = 'each';
} else if (block.type === 'AwaitBlock') { }
else if (block.type === 'AwaitBlock') {
expected = 'await'; expected = 'await';
} else if (block.type === 'KeyBlock') { }
else if (block.type === 'KeyBlock') {
expected = 'key'; expected = 'key';
} else { }
else {
parser.error(parser_errors.unexpected_block_close); parser.error(parser_errors.unexpected_block_close);
} }
parser.eat(expected, true); parser.eat(expected, true);
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
while (block.elseif) { while (block.elseif) {
block.end = parser.index; block.end = parser.index;
parser.stack.pop(); parser.stack.pop();
block = parser.current(); block = parser.current();
if (block.else) { if (block.else) {
block.else.end = start; block.else.end = start;
} }
} }
// strip leading/trailing whitespace as necessary // strip leading/trailing whitespace as necessary
const char_before = parser.template[block.start - 1]; const char_before = parser.template[block.start - 1];
const char_after = parser.template[parser.index]; const char_after = parser.template[parser.index];
const trim_before = !char_before || regex_whitespace.test(char_before); const trim_before = !char_before || regex_whitespace.test(char_before);
const trim_after = !char_after || regex_whitespace.test(char_after); const trim_after = !char_after || regex_whitespace.test(char_after);
trim_whitespace(block, trim_before, trim_after); trim_whitespace(block, trim_before, trim_after);
block.end = parser.index; block.end = parser.index;
parser.stack.pop(); parser.stack.pop();
} else if (parser.eat(':else')) { }
else if (parser.eat(':else')) {
if (parser.eat('if')) { if (parser.eat('if')) {
parser.error(parser_errors.invalid_elseif); parser.error(parser_errors.invalid_elseif);
} }
parser.allow_whitespace(); parser.allow_whitespace();
// :else if // :else if
if (parser.eat('if')) { if (parser.eat('if')) {
const block = parser.current(); const block = parser.current();
if (block.type !== 'IfBlock') { if (block.type !== 'IfBlock') {
parser.error( parser.error(parser.stack.some((block) => block.type === 'IfBlock')
parser.stack.some((block) => block.type === 'IfBlock')
? parser_errors.invalid_elseif_placement_unclosed_block(to_string(block)) ? parser_errors.invalid_elseif_placement_unclosed_block(to_string(block))
: parser_errors.invalid_elseif_placement_outside_if : parser_errors.invalid_elseif_placement_outside_if);
);
} }
parser.require_whitespace(); parser.require_whitespace();
const expression = read_expression(parser); const expression = read_expression(parser);
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
block.else = { block.else = {
start: parser.index, start: parser.index,
end: null, end: null,
@ -141,96 +127,84 @@ export default function mustache(parser: Parser) {
} }
] ]
}; };
parser.stack.push(block.else.children[0]); parser.stack.push(block.else.children[0]);
} else { }
else {
// :else // :else
const block = parser.current(); const block = parser.current();
if (block.type !== 'IfBlock' && block.type !== 'EachBlock') { if (block.type !== 'IfBlock' && block.type !== 'EachBlock') {
parser.error( parser.error(parser.stack.some((block) => block.type === 'IfBlock' || block.type === 'EachBlock')
parser.stack.some((block) => block.type === 'IfBlock' || block.type === 'EachBlock')
? parser_errors.invalid_else_placement_unclosed_block(to_string(block)) ? parser_errors.invalid_else_placement_unclosed_block(to_string(block))
: parser_errors.invalid_else_placement_outside_if : parser_errors.invalid_else_placement_outside_if);
);
} }
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
block.else = { block.else = {
start: parser.index, start: parser.index,
end: null, end: null,
type: 'ElseBlock', type: 'ElseBlock',
children: [] children: []
}; };
parser.stack.push(block.else); parser.stack.push(block.else);
} }
} else if (parser.match(':then') || parser.match(':catch')) { }
else if (parser.match(':then') || parser.match(':catch')) {
const block = parser.current(); const block = parser.current();
const is_then = parser.eat(':then') || !parser.eat(':catch'); const is_then = parser.eat(':then') || !parser.eat(':catch');
if (is_then) { if (is_then) {
if (block.type !== 'PendingBlock') { if (block.type !== 'PendingBlock') {
parser.error( parser.error(parser.stack.some((block) => block.type === 'PendingBlock')
parser.stack.some((block) => block.type === 'PendingBlock')
? parser_errors.invalid_then_placement_unclosed_block(to_string(block)) ? parser_errors.invalid_then_placement_unclosed_block(to_string(block))
: parser_errors.invalid_then_placement_without_await : parser_errors.invalid_then_placement_without_await);
);
} }
} else { }
else {
if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') { if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') {
parser.error( parser.error(parser.stack.some((block) => block.type === 'ThenBlock' || block.type === 'PendingBlock')
parser.stack.some((block) => block.type === 'ThenBlock' || block.type === 'PendingBlock')
? parser_errors.invalid_catch_placement_unclosed_block(to_string(block)) ? parser_errors.invalid_catch_placement_unclosed_block(to_string(block))
: parser_errors.invalid_catch_placement_without_await : parser_errors.invalid_catch_placement_without_await);
);
} }
} }
block.end = start; block.end = start;
parser.stack.pop(); parser.stack.pop();
const await_block = parser.current(); const await_block = parser.current();
if (!parser.eat('}')) { if (!parser.eat('}')) {
parser.require_whitespace(); parser.require_whitespace();
await_block[is_then ? 'value' : 'error'] = read_context(parser); await_block[is_then ? 'value' : 'error'] = read_context(parser);
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
} }
const new_block = {
const new_block: TemplateNode = {
start, start,
end: null, end: null,
type: is_then ? 'ThenBlock' : 'CatchBlock', type: is_then ? 'ThenBlock' : 'CatchBlock',
children: [], children: [],
skip: false skip: false
}; };
await_block[is_then ? 'then' : 'catch'] = new_block; await_block[is_then ? 'then' : 'catch'] = new_block;
parser.stack.push(new_block); parser.stack.push(new_block);
} else if (parser.eat('#')) { }
else if (parser.eat('#')) {
// {#if foo}, {#each foo} or {#await foo} // {#if foo}, {#each foo} or {#await foo}
let type; let type;
if (parser.eat('if')) { if (parser.eat('if')) {
type = 'IfBlock'; type = 'IfBlock';
} else if (parser.eat('each')) { }
else if (parser.eat('each')) {
type = 'EachBlock'; type = 'EachBlock';
} else if (parser.eat('await')) { }
else if (parser.eat('await')) {
type = 'AwaitBlock'; type = 'AwaitBlock';
} else if (parser.eat('key')) { }
else if (parser.eat('key')) {
type = 'KeyBlock'; type = 'KeyBlock';
} else { }
else {
parser.error(parser_errors.expected_block_type); parser.error(parser_errors.expected_block_type);
} }
parser.require_whitespace(); parser.require_whitespace();
const expression = read_expression(parser); const expression = read_expression(parser);
const block = type === 'AwaitBlock'
const block: TemplateNode =
type === 'AwaitBlock'
? { ? {
start, start,
end: null, end: null,
@ -267,154 +241,132 @@ export default function mustache(parser: Parser) {
expression, expression,
children: [] children: []
}; };
parser.allow_whitespace(); parser.allow_whitespace();
// {#each} blocks must declare a context {#each list as item} // {#each} blocks must declare a context {#each list as item}
if (type === 'EachBlock') { if (type === 'EachBlock') {
parser.eat('as', true); parser.eat('as', true);
parser.require_whitespace(); parser.require_whitespace();
block.context = read_context(parser); block.context = read_context(parser);
parser.allow_whitespace(); parser.allow_whitespace();
if (parser.eat(',')) { if (parser.eat(',')) {
parser.allow_whitespace(); parser.allow_whitespace();
block.index = parser.read_identifier(); block.index = parser.read_identifier();
if (!block.index) parser.error(parser_errors.expected_name); if (!block.index)
parser.error(parser_errors.expected_name);
parser.allow_whitespace(); parser.allow_whitespace();
} }
if (parser.eat('(')) { if (parser.eat('(')) {
parser.allow_whitespace(); parser.allow_whitespace();
block.key = read_expression(parser); block.key = read_expression(parser);
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat(')', true); parser.eat(')', true);
parser.allow_whitespace(); parser.allow_whitespace();
} }
} }
const await_block_shorthand = type === 'AwaitBlock' && parser.eat('then'); const await_block_shorthand = type === 'AwaitBlock' && parser.eat('then');
if (await_block_shorthand) { if (await_block_shorthand) {
if (parser.match_regex(regex_whitespace_with_closing_curly_brace)) { if (parser.match_regex(regex_whitespace_with_closing_curly_brace)) {
parser.allow_whitespace(); parser.allow_whitespace();
} else { }
else {
parser.require_whitespace(); parser.require_whitespace();
block.value = read_context(parser); block.value = read_context(parser);
parser.allow_whitespace(); parser.allow_whitespace();
} }
} }
const await_block_catch_shorthand = !await_block_shorthand && type === 'AwaitBlock' && parser.eat('catch');
const await_block_catch_shorthand =
!await_block_shorthand && type === 'AwaitBlock' && parser.eat('catch');
if (await_block_catch_shorthand) { if (await_block_catch_shorthand) {
if (parser.match_regex(regex_whitespace_with_closing_curly_brace)) { if (parser.match_regex(regex_whitespace_with_closing_curly_brace)) {
parser.allow_whitespace(); parser.allow_whitespace();
} else { }
else {
parser.require_whitespace(); parser.require_whitespace();
block.error = read_context(parser); block.error = read_context(parser);
parser.allow_whitespace(); parser.allow_whitespace();
} }
} }
parser.eat('}', true); parser.eat('}', true);
parser.current().children.push(block); parser.current().children.push(block);
parser.stack.push(block); parser.stack.push(block);
if (type === 'AwaitBlock') { if (type === 'AwaitBlock') {
let child_block; let child_block;
if (await_block_shorthand) { if (await_block_shorthand) {
block.then.skip = false; block.then.skip = false;
child_block = block.then; child_block = block.then;
} else if (await_block_catch_shorthand) { }
else if (await_block_catch_shorthand) {
block.catch.skip = false; block.catch.skip = false;
child_block = block.catch; child_block = block.catch;
} else { }
else {
block.pending.skip = false; block.pending.skip = false;
child_block = block.pending; child_block = block.pending;
} }
child_block.start = parser.index; child_block.start = parser.index;
parser.stack.push(child_block); parser.stack.push(child_block);
} }
} else if (parser.eat('@html')) { }
else if (parser.eat('@html')) {
// {@html content} tag // {@html content} tag
parser.require_whitespace(); parser.require_whitespace();
const expression = read_expression(parser); const expression = read_expression(parser);
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
parser.current().children.push({ parser.current().children.push({
start, start,
end: parser.index, end: parser.index,
type: 'RawMustacheTag', type: 'RawMustacheTag',
expression expression
}); });
} else if (parser.eat('@debug')) { }
else if (parser.eat('@debug')) {
let identifiers; let identifiers;
// Implies {@debug} which indicates "debug all" // Implies {@debug} which indicates "debug all"
if (parser.read(regex_whitespace_with_closing_curly_brace)) { if (parser.read(regex_whitespace_with_closing_curly_brace)) {
identifiers = []; identifiers = [];
} else { }
else {
const expression = read_expression(parser); const expression = read_expression(parser);
identifiers = identifiers =
expression.type === 'SequenceExpression' ? expression.expressions : [expression]; expression.type === 'SequenceExpression' ? expression.expressions : [expression];
identifiers.forEach((node) => { identifiers.forEach((node) => {
if (node.type !== 'Identifier') { if (node.type !== 'Identifier') {
parser.error(parser_errors.invalid_debug_args, node.start); parser.error(parser_errors.invalid_debug_args, node.start);
} }
}); });
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
} }
parser.current().children.push({ parser.current().children.push({
start, start,
end: parser.index, end: parser.index,
type: 'DebugTag', type: 'DebugTag',
identifiers identifiers
}); });
} else if (parser.eat('@const')) { }
else if (parser.eat('@const')) {
// {@const a = b} // {@const a = b}
parser.require_whitespace(); parser.require_whitespace();
const expression = read_expression(parser); const expression = read_expression(parser);
if (!(expression.type === 'AssignmentExpression' && expression.operator === '=')) { if (!(expression.type === 'AssignmentExpression' && expression.operator === '=')) {
parser.error( parser.error({
{
code: 'invalid-const-args', code: 'invalid-const-args',
message: '{@const ...} must be an assignment.' message: '{@const ...} must be an assignment.'
}, }, start);
start
);
} }
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
parser.current().children.push({ parser.current().children.push({
start, start,
end: parser.index, end: parser.index,
type: 'ConstTag', type: 'ConstTag',
expression expression
}); });
} else { }
else {
const expression = read_expression(parser); const expression = read_expression(parser);
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
parser.current().children.push({ parser.current().children.push({
start, start,
end: parser.index, end: parser.index,
@ -423,3 +375,7 @@ export default function mustache(parser: Parser) {
}); });
} }
} }

Loading…
Cancel
Save