UNPKG

@riotjs/compiler

Version:

Compiler for Riot.js .riot files

253 lines (228 loc) 7.22 kB
import { IS_CUSTOM_NODE, IS_SPREAD_ATTRIBUTE, IS_VOID_NODE, PROGRESS_TAG_NODE_NAME, SLOT_ATTRIBUTE, SLOT_TAG_NODE_NAME, TEMPLATE_TAG_NODE_NAME, } from './constants.js' import { findAttribute, findEachAttribute, findIfAttribute, findIsAttribute, findKeyAttribute, } from './find.js' import { getName, getNodeAttributes } from './utils.js' import { isBrowserAPI, isBuiltinAPI, isNewExpression, isRaw, } from '../../utils/ast-nodes-checks.js' import compose from 'cumpa' import { isNil } from '@riotjs/util/checks' import { nodeTypes } from '@riotjs/parser' import { types } from '../../utils/build-types.js' /** * True if the node has not expression set nor bindings directives * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true only if it's a static node that doesn't need bindings or expressions */ export function isStaticNode(node) { return [ hasExpressions, findEachAttribute, findIfAttribute, isCustomNode, isSlotNode, ].every((test) => !test(node)) } /** * Check if a node should be rendered in the final component HTML * For example slot <template slot="content"> tags not using `each` or `if` directives can be removed * see also https://github.com/riot/riot/issues/2888 * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true if we can remove this tag from the component rendered HTML */ export function isRemovableNode(node) { return ( isTemplateNode(node) && !isNil(findAttribute(SLOT_ATTRIBUTE, node)) && !hasEachAttribute(node) && !hasIfAttribute(node) ) } /** * Check if a node name is part of the browser or builtin javascript api or it belongs to the current scope * @param { types.NodePath } path - containing the current node visited * @returns {boolean} true if it's a global api variable */ export function isGlobal({ scope, node }) { // recursively find the identifier of this AST path if (node.object) { return isGlobal({ node: node.object, scope }) } return Boolean( isRaw(node) || isBuiltinAPI(node) || isBrowserAPI(node) || isNewExpression(node) || isNodeInScope(scope, node), ) } /** * Checks if the identifier of a given node exists in a scope * @param {Scope} scope - scope where to search for the identifier * @param {types.Node} node - node to search for the identifier * @returns {boolean} true if the node identifier is defined in the given scope */ function isNodeInScope(scope, node) { const traverse = (isInScope = false) => { types.visit(node, { visitIdentifier(path) { if (scope.lookup(getName(path.node))) { isInScope = true } this.abort() }, }) return isInScope } return traverse() } /** * True if the node has the isCustom attribute set * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true if either it's a riot component or a custom element */ export function isCustomNode(node) { return !!(node[IS_CUSTOM_NODE] || hasIsAttribute(node)) } /** * True the node is <slot> * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true if it's a slot node */ export function isSlotNode(node) { return node.name === SLOT_TAG_NODE_NAME } /** * True if the node has the isVoid attribute set * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true if the node is self closing */ export function isVoidNode(node) { return !!node[IS_VOID_NODE] } /** * True if the riot parser did find a tag node * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true only for the tag nodes */ export function isTagNode(node) { return node.type === nodeTypes.TAG } /** * True if the riot parser did find a text node * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true only for the text nodes */ export function isTextNode(node) { return node.type === nodeTypes.TEXT } /** * True if the node parsed any of the root nodes (each, tag bindings create root nodes as well...) * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true only for the root nodes */ export function isRootNode(node) { return node.isRoot } /** * True if the node parsed is the absolute root node (nested root nodes are not considered) * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true only for the root nodes */ export function isAbsoluteRootNode(node) { return node.isRoot && !node.isNestedRoot } /** * True if the attribute parsed is of type spread one * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true if the attribute node is of type spread */ export function isSpreadAttribute(node) { return node[IS_SPREAD_ATTRIBUTE] } /** * True if the node is an attribute and its name is "value" * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true only for value attribute nodes */ export function isValueAttribute(node) { return node.name === 'value' } /** * True if the DOM node is a progress tag * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true for the progress tags */ export function isProgressNode(node) { return node.name === PROGRESS_TAG_NODE_NAME } /** * True if the DOM node is a <template> tag * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true for the progress tags */ export function isTemplateNode(node) { return node.name === TEMPLATE_TAG_NODE_NAME } /** * True if the node is an attribute and a DOM handler * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true only for dom listener attribute nodes */ export const isEventAttribute = (() => { const EVENT_ATTR_RE = /^on/ return (node) => EVENT_ATTR_RE.test(node.name) })() /** * Check if a string is an html comment * @param {string} string - test string * @returns {boolean} true if html comment */ export function isCommentString(string) { return string.trim().indexOf('<!') === 0 } /** * True if the node has expressions or expression attributes * @param {RiotParser.Node} node - riot parser node * @returns {boolean} ditto */ export function hasExpressions(node) { return !!( node.expressions || // has expression attributes getNodeAttributes(node).some((attribute) => hasExpressions(attribute)) || // has child text nodes with expressions (node.nodes && node.nodes.some((node) => isTextNode(node) && hasExpressions(node))) ) } /** * True if the node is a directive having its own template or it's a slot node * @param {RiotParser.Node} node - riot parser node * @returns {boolean} true only for the IF EACH and TAG bindings or it's a slot node */ export function hasItsOwnTemplate(node) { return [findEachAttribute, findIfAttribute, isCustomNode, isSlotNode].some( (test) => test(node), ) } export const hasIfAttribute = compose(Boolean, findIfAttribute) export const hasEachAttribute = compose(Boolean, findEachAttribute) export const hasIsAttribute = compose(Boolean, findIsAttribute) export const hasKeyAttribute = compose(Boolean, findKeyAttribute)