src/generators/template/bindings/each.js
import {
BINDING_CONDITION_KEY,
BINDING_EVALUATE_KEY,
BINDING_GET_KEY_KEY,
BINDING_INDEX_NAME_KEY,
BINDING_ITEM_NAME_KEY,
BINDING_TYPES,
BINDING_TYPE_KEY,
EACH_BINDING_TYPE,
} from '../constants.js'
import {
createASTFromExpression,
createSelectorProperties,
createTemplateProperty,
getAttributeExpression,
getName,
toScopedFunction,
} from '../utils.js'
import {
findEachAttribute,
findIfAttribute,
findKeyAttribute,
} from '../find.js'
import {
isExpressionStatement,
isSequenceExpression,
} from '../../../utils/ast-nodes-checks.js'
import {
nullNode,
simplePropertyNode,
} from '../../../utils/custom-ast-nodes.js'
import { builders } from '../../../utils/build-types.js'
import compose from 'cumpa'
import { createNestedBindings } from '../builder.js'
import generateJavascript from '../../../utils/generate-javascript.js'
import { panic } from '@riotjs/util/misc.js'
const getEachItemName = (expression) =>
isSequenceExpression(expression.left)
? expression.left.expressions[0]
: expression.left
const getEachIndexName = (expression) =>
isSequenceExpression(expression.left) ? expression.left.expressions[1] : null
const getEachValue = (expression) => expression.right
const nameToliteral = compose(builders.literal, getName)
const generateEachItemNameKey = (expression) =>
simplePropertyNode(
BINDING_ITEM_NAME_KEY,
compose(nameToliteral, getEachItemName)(expression),
)
const generateEachIndexNameKey = (expression) =>
simplePropertyNode(
BINDING_INDEX_NAME_KEY,
compose(nameToliteral, getEachIndexName)(expression),
)
const generateEachEvaluateKey = (
expression,
eachExpression,
sourceFile,
sourceCode,
) =>
simplePropertyNode(
BINDING_EVALUATE_KEY,
compose(
(e) => toScopedFunction(e, sourceFile, sourceCode),
(e) => ({
...eachExpression,
text: generateJavascript(e).code,
}),
getEachValue,
)(expression),
)
/**
* Get the each expression properties to create properly the template binding
* @param { DomBinding.Expression } eachExpression - original each expression data
* @param { string } sourceFile - original tag file
* @param { string } sourceCode - original tag source code
* @returns { Array } AST nodes that are needed to build an each binding
*/
export function generateEachExpressionProperties(
eachExpression,
sourceFile,
sourceCode,
) {
const ast = createASTFromExpression(eachExpression, sourceFile, sourceCode)
const body = ast.program.body
const firstNode = body[0]
if (!isExpressionStatement(firstNode)) {
panic(
`The each directives supported should be of type "ExpressionStatement",you have provided a "${firstNode.type}"`,
)
}
const { expression } = firstNode
return [
generateEachItemNameKey(expression),
generateEachIndexNameKey(expression),
generateEachEvaluateKey(expression, eachExpression, sourceFile, sourceCode),
]
}
/**
* Transform a RiotParser.Node.Tag into an each binding
* @param { RiotParser.Node.Tag } sourceNode - tag containing the each attribute
* @param { string } selectorAttribute - attribute needed to select the target node
* @param { string } sourceFile - source file path
* @param { string } sourceCode - original source
* @returns { AST.Node } an each binding node
*/
export default function createEachBinding(
sourceNode,
selectorAttribute,
sourceFile,
sourceCode,
) {
const [ifAttribute, eachAttribute, keyAttribute] = [
findIfAttribute,
findEachAttribute,
findKeyAttribute,
].map((f) => f(sourceNode))
const attributeOrNull = (attribute) =>
attribute
? toScopedFunction(
getAttributeExpression(attribute),
sourceFile,
sourceCode,
)
: nullNode()
return builders.objectExpression([
simplePropertyNode(
BINDING_TYPE_KEY,
builders.memberExpression(
builders.identifier(BINDING_TYPES),
builders.identifier(EACH_BINDING_TYPE),
false,
),
),
simplePropertyNode(BINDING_GET_KEY_KEY, attributeOrNull(keyAttribute)),
simplePropertyNode(BINDING_CONDITION_KEY, attributeOrNull(ifAttribute)),
createTemplateProperty(
createNestedBindings(
sourceNode,
sourceFile,
sourceCode,
selectorAttribute,
),
),
...createSelectorProperties(selectorAttribute),
...compose(
generateEachExpressionProperties,
getAttributeExpression,
)(eachAttribute),
])
}