import { assertNever } from "@opticss/util" ; import { CompoundSelector , postcssSelectorParser as selectorParser } from "opticss" ; import { ATTR_PRESENT , AttrToken , RESERVED_NAMESPACES , ROOT_CLASS } from "../BlockSyntax" ; import { AttrValue , Block , BlockClass } from "../BlockTree" ; export enum BlockType { root , attribute , class , classAttribute } export type RootAttributeNode = { blockName ? : string ; blockType : BlockType . attribute ; node : selectorParser . Attribute ; } ; export type ClassAttributeNode = { blockName ? : string ; blockType : BlockType . classAttribute ; node : selectorParser . Attribute ; } ; export type AttributeNode = RootAttributeNode | ClassAttributeNode ; export type RootClassNode = { blockName ? : string ; blockType : BlockType . root ; node : selectorParser . Pseudo ; } ; export type BlockClassNode = { blockName ? : string ; blockType : BlockType . class ; node : selectorParser . ClassName ; } ; export type ClassNode = RootClassNode | BlockClassNode ; export type NodeAndType = AttributeNode | ClassNode ; /** Extract an Attribute's value from a `selectorParser` attribute selector */ function attrValue ( attr ) { if ( attr . value ) { return attr . value . replace ( /^(["'])(.+(?=\1$))\1$/ , "$2" ) ; } else { return ATTR_PRESENT ; } } /** Extract an Attribute's name from an attribute selector */ export function toAttrToken ( attr ) { return { namespace : attr . namespaceString , name : attr . attribute , value : attrValue ( attr ) , quoted : ! ! attr . quoted , } ; } param t param options return export function blockTypeName ( t , options ? ) { let isPlural = options && options . plural ; switch ( t ) { case BlockType . root : return isPlural ? "block roots" : "block root" ; case BlockType . attribute : return isPlural ? "root-level states" : "root-level state" ; case BlockType . class : return isPlural ? "classes" : "class" ; case BlockType . classAttribute : return isPlural ? "class states" : "class state" ; default : return assertNever ( t ) ; } } param object export function isExternalBlock ( object ) { return ( object . blockType === BlockType . attribute && ! ! object . blockName ) ; } param object export function isRootLevelObject ( object ) { if ( object . blockType === BlockType . attribute && object . blockName ) return false ; return object . blockType === BlockType . root || object . blockType === BlockType . attribute ; } param object export function isClassLevelObject ( object ) { return object . blockType === BlockType . class || object . blockType === BlockType . classAttribute ; } /**
 * Check if given selector node is targeting the root block node
 */ export function isRootNode ( node ) { return selectorParser . isPseudoClass ( node ) && node . value === ROOT_CLASS ; } export const isClassNode = selectorParser . isClassName ; param node return export function isAttributeNode ( node ) { return selectorParser . isAttribute ( node ) && node . namespace !== true && ! RESERVED_NAMESPACES . has ( node . namespace ) ; } /**
 * Describes all possible terminating styles in a CSS Blocks selector.
 */ export interface StyleTargets { blockAttrs : AttrValue [ ] ; blockClasses : BlockClass [ ] ; } param block param sel returns export function getStyleTargets ( block , sel ) { let blockAttrs = [ ] ; let blockClass = undefined ; for ( let node of sel . nodes ) { if ( isRootNode ( node ) ) { let nextNode = node . next ( ) ; if ( nextNode && isAttributeNode ( nextNode ) && typeof nextNode . namespace === "string" ) { break ; } else { blockClass = block . rootClass ; } } else if ( isClassNode ( node ) ) { blockClass = block . ensureClass ( node . value ) ; } else if ( isAttributeNode ( node ) ) { if ( ! blockClass ) { break ; } blockAttrs . push ( blockClass . ensureAttributeValue ( toAttrToken ( node ) ) ) ; } } return { blockAttrs , blockClasses : blockClass ? [ blockClass ] : [ ] , } ; }