twing
Version:
First-class Twig engine for Node.js
101 lines (100 loc) • 4.83 kB
JavaScript
;
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;