UNPKG

twing

Version:

First-class Twig engine for Node.js

164 lines (163 loc) 7.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.executeCallNodeSynchronously = exports.executeCallNode = void 0; const runtime_1 = require("../../error/runtime"); const traceable_method_1 = require("../../helpers/traceable-method"); const constant_1 = require("../../node/expression/constant"); const get_key_value_pairs_1 = require("../../helpers/get-key-value-pairs"); const get_test_1 = require("../../helpers/get-test"); const get_function_1 = require("../../helpers/get-function"); const get_filter_1 = require("../../helpers/get-filter"); const array_merge = require('locutus/php/array/array_merge'); const snakeCase = require('snake-case'); const normalizeName = (name) => { return snakeCase(name).toLowerCase(); }; const getArguments = (node, template, argumentsNode, acceptedArguments, isVariadic) => { const callType = node.type; const callName = node.attributes.operatorName; const parameters = new Map(); let named = false; const keyPairs = (0, get_key_value_pairs_1.getKeyValuePairs)(argumentsNode); for (let { key, value } of keyPairs) { let name = key.attributes.value; if (typeof name === "string") { named = true; name = normalizeName(name); } else if (named) { throw (0, runtime_1.createRuntimeError)(`Positional arguments cannot be used after named arguments for ${callType} "${callName}".`, node, template.source); } parameters.set(name, { key, value }); } const callableParameters = acceptedArguments; const names = []; let optionalArguments = []; let arguments_ = []; let position = 0; for (const callableParameter of callableParameters) { const name = '' + normalizeName(callableParameter.name); names.push(name); const parameter = parameters.get(name); if (parameter) { if (parameters.has(position)) { throw (0, runtime_1.createRuntimeError)(`Argument "${name}" is defined twice for ${callType} "${callName}".`, node, template.source); } arguments_ = array_merge(arguments_, optionalArguments); arguments_.push(parameter.value); parameters.delete(name); optionalArguments = []; } else { const parameter = parameters.get(position); if (parameter) { arguments_ = array_merge(arguments_, optionalArguments); arguments_.push(parameter.value); parameters.delete(position); optionalArguments = []; ++position; } else if (callableParameter.defaultValue !== undefined) { arguments_.push((0, constant_1.createConstantNode)(callableParameter.defaultValue, node.line, node.column)); } else { throw (0, runtime_1.createRuntimeError)(`Value for argument "${name}" is required for ${callType} "${callName}".`, node, template.source); } } } if (isVariadic) { const resolvedKeys = []; const arbitraryArguments = []; for (const [key, value] of parameters) { arbitraryArguments.push(value.value); resolvedKeys.push(key); } for (const key of resolvedKeys) { parameters.delete(key); } if (arbitraryArguments.length) { arguments_ = array_merge(arguments_, optionalArguments); arguments_.push(...arbitraryArguments); } } if (parameters.size > 0) { const unknownParameter = [...parameters.values()][0]; throw (0, runtime_1.createRuntimeError)(`Unknown argument${parameters.size > 1 ? 's' : ''} "${[...parameters.keys()].join('", "')}" for ${callType} "${callName}(${names.join(', ')})".`, unknownParameter.key, template.source); } return arguments_; }; const executeCallNode = async (node, executionContext) => { const { type } = node; const { template, environment, nodeExecutor: execute } = executionContext; const { operatorName } = node.attributes; let callableWrapper; switch (type) { case "filter": callableWrapper = (0, get_filter_1.getFilter)(environment.filters, operatorName); break; case "function": callableWrapper = (0, get_function_1.getFunction)(environment.functions, operatorName); break; // for some reason, using `case "test"` makes the compiler assume that callableWrapper is used // before it is assigned a value; this is probably a bug of the compiler default: callableWrapper = (0, get_test_1.getTest)(environment.tests, operatorName); break; } if (callableWrapper === null) { throw (0, runtime_1.createRuntimeError)(`Unknown ${type} "${operatorName}".`, node, template.source); } const { operand, arguments: callArguments } = node.children; const argumentNodes = getArguments(node, template, callArguments, callableWrapper.acceptedArguments, callableWrapper.isVariadic); const actualArguments = []; actualArguments.push(...callableWrapper.nativeArguments); if (operand) { actualArguments.push(await execute(operand, executionContext)); } const providedArguments = await Promise.all([ ...argumentNodes.map((node) => execute(node, executionContext)) ]); actualArguments.push(...providedArguments); const traceableCallable = (0, traceable_method_1.getTraceableMethod)(callableWrapper.callable, node, template.source); return traceableCallable(executionContext, ...actualArguments).then((value) => { return value; }); }; exports.executeCallNode = executeCallNode; const executeCallNodeSynchronously = (node, executionContext) => { const { type } = node; const { template, environment, nodeExecutor: execute } = executionContext; const { operatorName } = node.attributes; let callableWrapper; switch (type) { case "filter": callableWrapper = (0, get_filter_1.getFilter)(environment.filters, operatorName); break; case "function": callableWrapper = (0, get_function_1.getFunction)(environment.functions, operatorName); break; // for some reason, using `case "test"` makes the compiler assume that callableWrapper is used // before it is assigned a value; this is probably a bug of the compiler default: callableWrapper = (0, get_test_1.getTest)(environment.tests, operatorName); break; } if (callableWrapper === null) { throw (0, runtime_1.createRuntimeError)(`Unknown ${type} "${operatorName}".`, node, template.source); } const { operand, arguments: callArguments } = node.children; const argumentNodes = getArguments(node, template, callArguments, callableWrapper.acceptedArguments, callableWrapper.isVariadic); const actualArguments = []; actualArguments.push(...callableWrapper.nativeArguments); if (operand) { actualArguments.push(execute(operand, executionContext)); } const providedArguments = argumentNodes.map((node) => execute(node, executionContext)); actualArguments.push(...providedArguments); const traceableCallable = (0, traceable_method_1.getSynchronousTraceableMethod)(callableWrapper.callable, node, template.source); return traceableCallable(executionContext, ...actualArguments); }; exports.executeCallNodeSynchronously = executeCallNodeSynchronously;