UNPKG

twing

Version:

First-class Twig engine for Node.js

101 lines (100 loc) 4.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createForTagHandler = void 0; /** * Loops over each item of a sequence. * * <pre> * <ul> * {% for user in users %} * <li>{{ user.username|e }}</li> * {% endfor %} * </ul> * </pre> */ const node_1 = require("../node"); const parsing_1 = require("../error/parsing"); const assignment_1 = require("../node/expression/assignment"); const for_1 = require("../node/for"); const createForTagHandler = () => { const tag = 'for'; const decideForFork = (token) => { return token.test("NAME", ['else', 'endfor']); }; const decideForEnd = (token) => { return token.test("NAME", 'endfor'); }; // the loop variable cannot be used in the condition const checkLoopUsageCondition = (stream, node) => { if ((node.type === "attribute_accessor") && (node.children.target.type === "name") && (node.children.target.attributes.name === 'loop')) { throw (0, parsing_1.createParsingError)('The "loop" variable cannot be used in a looping condition.', node, stream.source); } for (const [, child] of (0, node_1.getChildren)(node)) { checkLoopUsageCondition(stream, child); } }; // check usage of non-defined loop-items // it does not catch all problems (for instance when a for is included into another or when the variable is used in an include) const checkLoopUsageBody = (stream, node) => { if ((node.type === "attribute_accessor") && (node.children.target.type === "name") && (node.children.target.attributes.name === "loop")) { const { attribute } = node.children; if (attribute.type === "constant" && (['length', 'revindex0', 'revindex', 'last'].indexOf(attribute.attributes.value) > -1)) { throw (0, parsing_1.createParsingError)(`The "loop.${attribute.attributes.value}" variable is not defined when looping with a condition.`, node, stream.source); } } // should check for parent.loop.XXX usage if (node.type === "for") { return; } for (let [, child] of (0, node_1.getChildren)(node)) { checkLoopUsageBody(stream, child); } }; return { tag, initialize: (parser, level) => { return (token, stream) => { const { line, column } = token; const targets = parser.parseAssignmentExpression(stream); stream.expect("OPERATOR", 'in'); let sequence = parser.parseExpression(stream); let ifExpression = null; if ((level < 3) && stream.nextIf("NAME", 'if')) { console.warn(`Using an "if" condition on "for" tag in "${stream.source.name}" at line ${line} is deprecated since Twig 2.10.0, use a "filter" filter or an "if" condition inside the "for" body instead (if your condition depends on a variable updated inside the loop).`); ifExpression = parser.parseExpression(stream); } stream.expect("TAG_END"); let body = parser.subparse(stream, tag, decideForFork); let elseToken; if (stream.next().value == 'else') { stream.expect("TAG_END"); elseToken = parser.subparse(stream, tag, decideForEnd); stream.next(); } else { elseToken = null; } stream.expect("TAG_END"); let keyTarget; let valueTarget; if ((0, node_1.getChildrenCount)(targets) > 1) { keyTarget = targets.children[0]; keyTarget = (0, assignment_1.createAssignmentNode)(keyTarget.attributes.name, keyTarget.line, keyTarget.column); valueTarget = targets.children[1]; valueTarget = (0, assignment_1.createAssignmentNode)(valueTarget.attributes.name, valueTarget.line, valueTarget.column); } else { keyTarget = (0, assignment_1.createAssignmentNode)('_key', line, column); valueTarget = targets.children[0]; valueTarget = (0, assignment_1.createAssignmentNode)(valueTarget.attributes.name, valueTarget.line, valueTarget.column); } if (ifExpression) { checkLoopUsageCondition(stream, ifExpression); checkLoopUsageBody(stream, body); } return (0, for_1.createForNode)(keyTarget, valueTarget, sequence, ifExpression, body, elseToken, line, column, tag); }; } }; }; exports.createForTagHandler = createForTagHandler;