import { includes , find , filter } from "lodash" ; import { XMLAttribute } from "@xml-tools/ast" ; import { isXMLNamespaceKey } from "@xml-tools/common" ; import { UI5SemanticModel , UI5Class , } from "@ui5-language-assistant/semantic-model-types" ; import { getUI5ClassByXMLElement , getUI5AggregationByXMLElement , flattenProperties , flattenEvents , flattenAssociations , flattenAggregations , ui5NodeToFQN , splitQNameByNamespace , } from "@ui5-language-assistant/logic-utils" ; import { UnknownAttributeKeyIssue } from "../../../api" ; import { TEMPLATING_NS , CUSTOM_DATA_NS , CORE_NS , } from "../../utils/special-namespaces" ; export function validateUnknownAttributeKey ( attribute , model ) { if ( attribute . syntax . key === undefined || attribute . key === null ) { return [ ] ; } if ( isAttributeNameAlwaysValid ( attribute ) ) { return [ ] ; } const xmlElement = attribute . parent ; const ui5Class = getUI5ClassByXMLElement ( xmlElement , model ) ; if ( ui5Class !== undefined ) { if ( isValidUI5ClassAttribute ( ui5Class , attribute ) ) { return [ ] ; } return [ { kind : "UnknownAttributeKey" , message : `Unknown attribute key: ${ attribute . key }` , offsetRange : { start : attribute . syntax . key . startOffset , end : attribute . syntax . key . endOffset , } , severity : "error" , } , ] ; } const ui5Aggregation = getUI5AggregationByXMLElement ( xmlElement , model ) ; if ( ui5Aggregation !== undefined ) { return [ { kind : "UnknownAttributeKey" , message : `Unknown attribute key: ${ attribute . key }` , offsetRange : { start : attribute . syntax . key . startOffset , end : attribute . syntax . key . endOffset , } , severity : "error" , } , ] ; } return [ ] ; } function isAttributeNameAlwaysValid ( attribute ) { if ( attribute . key === null ) { return true ; } if ( isXMLNamespaceKey ( { key : attribute . key , includeEmptyPrefix : false } ) ) { return true ; } const { ns , name } = splitAttributeByNamespace ( attribute as XMLAttribute & { key : string ; } ) ; const ALL_ATTRIBUTE_NAMES = undefined ; const ALWAYS_VALID_ATTRIBUTES = [ { ns : "sap.ui.dt" , name : "designtime" } , { ns : CORE_NS , name : "require" } , { ns : TEMPLATING_NS , name : "require" } , { ns : CUSTOM_DATA_NS , name : ALL_ATTRIBUTE_NAMES } , ] ; if ( find ( ALWAYS_VALID_ATTRIBUTES , ( validAttribute ) => validAttribute . ns === ns && ( validAttribute . name === ALL_ATTRIBUTE_NAMES || validAttribute . name === name ) ) !== undefined ) { return true ; } return false ; } function isValidUI5ClassAttribute ( ui5Class , attribute ) { const ALL_CLASSES = undefined ; const ALWAYS_VALID_CLASS_ATTRIBUTES = [ { name : "class" , classFQN : ALL_CLASSES } , { name : "binding" , classFQN : ALL_CLASSES } , { name : "stashed" , classFQN : "sap.uxap.ObjectPageLazyLoader" } , ] ; if ( find ( ALWAYS_VALID_CLASS_ATTRIBUTES , ( validAttribute ) => validAttribute . name === attribute . key && ( validAttribute . classFQN === ALL_CLASSES || validAttribute . classFQN === ui5NodeToFQN ( ui5Class ) ) ) !== undefined ) { return true ; } const allProps = flattenProperties ( ui5Class ) ; const allEvents = flattenEvents ( ui5Class ) ; const allAssociations = flattenAssociations ( ui5Class ) ; const allAggregations = flattenAggregations ( ui5Class ) ; const allClassUI5Attributes = allProps . concat ( allEvents ) . concat ( allAssociations ) . concat ( allAggregations ) ; const allowedVisibility = [ "public" , "protected" ] ; const publicClassUI5Attributes = filter ( allClassUI5Attributes , ( _ ) => includes ( allowedVisibility , _ . visibility ) ) ; if ( find ( publicClassUI5Attributes , ( _ ) => _ . name === attribute . key ) !== undefined ) { return true ; } return false ; } function splitAttributeByNamespace ( attribute ) { const { prefix , localName } = splitQNameByNamespace ( attribute . key ) ; if ( prefix === undefined ) { return { ns : prefix , name : localName } ; } const resolvedNS = attribute . parent . namespaces [ prefix ] ; return { ns : resolvedNS , name : localName , } ; }