UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

377 lines (344 loc) 13.5 kB
/*! * OpenUI5 * (c) Copyright 2009-2021 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ /** * Defines support rules related to the view. */ sap.ui.define(["sap/ui/support/library", "sap/ui/core/Element", "sap/ui/thirdparty/jquery", "sap/base/util/isEmptyObject"], function(SupportLib, Element, jQuery, isEmptyObject) { "use strict"; // shortcuts var Categories = SupportLib.Categories; // Accessibility, Performance, Memory, ... var Severity = SupportLib.Severity; // Hint, Warning, Error var Audiences = SupportLib.Audiences; // Control, Internal, Application //********************************************************** // Rule Definitions //********************************************************** /** * Checks for wrongly configured view namespace */ var oXMLViewWrongNamespace = { id: "xmlViewWrongNamespace", audiences: [Audiences.Application], categories: [Categories.Functionality], enabled: true, minversion: "-", title: "XML View is not configured with namespace 'sap.ui.core.mvc'", description: "For consistency and proper resource loading, the root node of an XML view must be configured with the namespace 'mvc'", resolution: "Define the XML view as '<mvc:View ...>' and configure the XML namepspace as 'xmlns:mvc=\"sap.ui.core.mvc\"'", resolutionurls: [{ text: "Documentation: Namespaces in XML Views", href: "https://sapui5.hana.ondemand.com/#/topic/2421a2c9fa574b2e937461b5313671f0" }], check: function(oIssueManager, oCoreFacade, oScope) { var aXMLViews = oScope.getElements().filter(function (oControl) { return oControl.getMetadata().getName() === "sap.ui.core.mvc.XMLView"; }); aXMLViews.forEach(function (oXMLView) { if (oXMLView._xContent.namespaceURI !== "sap.ui.core.mvc") { var sViewName = oXMLView.getViewName().split("\.").pop(); oIssueManager.addIssue({ severity: Severity.Medium, details: "The view '" + sViewName + "' (" + oXMLView.getId() + ") is configured with namespace '" + oXMLView._xContent.namespaceURI + "' instead of 'sap.ui.core.mvc'", context: { id: oXMLView.getId() } }); } }); } }; /** * Checks if a default namespaces is set in an XML view */ var oXMLViewDefaultNamespace = { id: "xmlViewDefaultNamespace", audiences: [Audiences.Control, Audiences.Application], categories: [Categories.Performance], enabled: true, minversion: "-", title: "Default namespace missing in XML view", description: "If the default namespace is missing, the code is less readable and parsing performance may be slow", resolution: "Set the namespace of the control library that holds most of the controls you use as default namespace (e.g. xmlns=\"sap.m\")", resolutionurls: [{ text: "Documentation: Namespaces in XML Views", href: "https://sapui5.hana.ondemand.com/#/topic/2421a2c9fa574b2e937461b5313671f0" }], check: function(oIssueManager, oCoreFacade, oScope) { var aXMLViews = oScope.getElements().filter(function (oControl) { return oControl.getMetadata().getName() === "sap.ui.core.mvc.XMLView"; }); aXMLViews.forEach(function (oXMLView) { if (!oXMLView._xContent.attributes.getNamedItem("xmlns")) { var sViewName = oXMLView.getViewName().split("\.").pop(); oIssueManager.addIssue({ severity: Severity.Low, details: "The view '" + sViewName + "' (" + oXMLView.getId() + ") does not contain a default namespace", context: { id: oXMLView.getId() } }); } }); } }; var oXMLViewLowerCaseControl = { id: "xmlViewLowerCaseControl", audiences: ["Control","Application"], categories: ["Performance"], enabled: true, minversion: "-", title: "Control tag in XML view starts with lower case", description: "Control tags with lower case cannot be loaded in Linux-based systems", resolution: "Start the Control tag with upper case", resolutionurls: [], check: function (oIssueManager, oCoreFacade, oScope) { //get all aggregations of each element var aAggregationsOfElements = oScope.getElements().map( function (oElement) { return Object.keys(oElement.getMetadata().getAllAggregations()); } ); //flatten array of arrays and filter duplicates var aAggregations = aAggregationsOfElements.reduce( function(a, b) { return a.concat(b); }).filter( function (x, i, a) { return a.indexOf(x) === i; }); var aXMLViews = oScope.getElements().filter(function (oControl) { return oControl.getMetadata().getName() === "sap.ui.core.mvc.XMLView"; }); aXMLViews.forEach(function (oXMLView) { var aLocalName = []; var _getTags = function (oXcontent) { aLocalName.push(oXcontent.localName); for (var i = 0; i < oXcontent.children.length; i++) { _getTags(oXcontent.children[i]); } }; _getTags(oXMLView._xContent); aLocalName = jQuery.uniqueSort(aLocalName); aLocalName.forEach(function (sTag) { var sFirstLetter = sTag.charAt(0); // check for lowercase, aggregations are excluded if ((sFirstLetter.toLowerCase() === sFirstLetter) && !aAggregations.includes(sTag)) { var sViewName = oXMLView.getViewName().split("\.").pop(); oIssueManager.addIssue({ severity: Severity.High, details: "View '" + sViewName + "' (" + oXMLView.getId() + ") contains a Control tag that starts with lower case '" + sTag + "'", context: { id: oXMLView.getId() } }); } }); }); } }; /** * Checks for unused namespaces inside an XML view */ var oXMLViewUnusedNamespaces = { id: "xmlViewUnusedNamespaces", audiences: [Audiences.Control, Audiences.Application], categories: [Categories.Usability], enabled: true, minversion: "-", title: "Unused namespaces in XML view", description: "Namespaces that are declared but not used may confuse readers of the code", resolution: "Remove the unused namespaces from the view definition", resolutionurls: [{ text: "Documentation: Namespaces in XML Views", href: "https://sapui5.hana.ondemand.com/#/topic/2421a2c9fa574b2e937461b5313671f0" }], check: function(oIssueManager, oCoreFacade, oScope) { var aXMLViews = oScope.getElements().filter(function (oControl) { return oControl.getMetadata().getName() === "sap.ui.core.mvc.XMLView"; }); aXMLViews.forEach(function (oXMLView) { for (var i = 0; i < oXMLView._xContent.attributes.length; i++) { var sName = oXMLView._xContent.attributes.item(i).name; var sLocalName = oXMLView._xContent.attributes.item(i).localName; var sFullName = oXMLView._xContent.attributes.item(i).value; // check all explicit namespaces except for the injected support namespace // and the mvc, because the use of mvc is checked in other rule if (sName.match("xmlns:") && sLocalName !== "xmlns:support" && sLocalName !== "mvc" && sFullName.indexOf("schemas.sap.com") < 0) { var oContent = jQuery(oXMLView._xContent)[0]; // get the xml code of the view as a string // The outerHTML doesn't work with IE, so we used // the XMLSerializer instead var sContent = new XMLSerializer().serializeToString(oContent); // check if there is a reference of this namespace inside the view if (!sContent.match("<" + sLocalName + ":") && !sContent.match(" " + sLocalName + ":")) { var sViewName = oXMLView.getViewName().split("\.").pop(); oIssueManager.addIssue({ severity: Severity.Medium, details: "View '" + sViewName + "' (" + oXMLView.getId() + ") contains an unused XML namespace '" + sLocalName + "' referencing library '" + sFullName + "'", context: { id: oXMLView.getId() } }); } } } }); } }; /** * Checks for deprecated properties */ var oDeprecatedPropertyRule = { id: "deprecatedProperty", audiences: [Audiences.Application], categories: [Categories.Functionality], enabled: true, minversion: "1.38", title: "Control is using deprecated property", description: "Using deprecated properties should be avoided, because they are not maintained anymore", resolution: "Refer to the API of the element which property should be used instead.", resolutionurls: [{ text: "API Reference", href: "https://sapui5.hana.ondemand.com/#/api/deprecated" }], check: function(oIssueManager, oCoreFacade, oScope) { oScope.getElementsByClassName(Element).forEach(function(oElement) { var oMetadata = oElement.getMetadata(), mProperties = oMetadata.getAllProperties(); for (var sProperty in mProperties) { // if property is deprecated and it is set to a different from the default value // Checks only the deprecated properties with defaultValue property is not null if (mProperties[sProperty].deprecated && mProperties[sProperty].defaultValue != oElement.getProperty(sProperty) && mProperties[sProperty].defaultValue !== null) { oIssueManager.addIssue({ severity: Severity.Medium, details: "Deprecated property '" + sProperty + "' is used for element '" + oElement.getId() + "'.", context: { id: oElement.getId() } }); } } }); } }; /** * Checks for deprecated aggregations */ var oDeprecatedAggregationRule = { id: "deprecatedAggregation", audiences: [Audiences.Application], categories: [Categories.Functionality], enabled: true, minversion: "1.38", title: "Control is using deprecated aggregation", description: "Using deprecated aggregation should be avoided, because they are not maintained anymore", resolution: "Refer to the API of the element which aggregation should be used instead.", resolutionurls: [{ text: "API Reference", href: "https://sapui5.hana.ondemand.com/#/api/deprecated" }], check: function(oIssueManager, oCoreFacade, oScope) { oScope.getElementsByClassName(Element).forEach(function(oElement) { var oMetadata = oElement.getMetadata(), mAggregations = oMetadata.getAllAggregations(); for (var sAggregation in mAggregations) { // if aggregation is deprecated and contains elements if (mAggregations[sAggregation].deprecated && !isEmptyObject(oElement.getAggregation(sAggregation))) { oIssueManager.addIssue({ severity: Severity.Medium, details: "Deprecated aggregation '" + sAggregation + "' is used for element '" + oElement.getId() + "'.", context: { id: oElement.getId() } }); } } }); } }; /** * Checks for deprecated associations */ var oDeprecatedAssociationRule = { id: "deprecatedAssociation", audiences: [Audiences.Application], categories: [Categories.Functionality], enabled: true, minversion: "1.38", title: "Control is using deprecated association", description: "Using deprecated association should be avoided, because they are not maintained anymore", resolution: "Refer to the API of the element which association should be used instead.", resolutionurls: [{ text: "API Reference", href: "https://sapui5.hana.ondemand.com/#/api/deprecated" }], check: function(oIssueManager, oCoreFacade, oScope) { oScope.getElementsByClassName(Element).forEach(function(oElement) { var oMetadata = oElement.getMetadata(), mAssociations = oMetadata.getAllAssociations(); for (var sAssociation in mAssociations) { // if association is deprecated and set by developer if (mAssociations[sAssociation].deprecated && !isEmptyObject(oElement.getAssociation(sAssociation))) { oIssueManager.addIssue({ severity: Severity.Medium, details: "Deprecated association '" + sAssociation + "' is used for element '" + oElement.getId() + "'.", context: { id: oElement.getId() } }); } } }); } }; /** * Checks for deprecated events */ var oDeprecatedEventRule = { id: "deprecatedEvent", audiences: [Audiences.Application], categories: [Categories.Functionality], enabled: true, minversion: "1.38", title: "Control is using deprecated event", description: "Using deprecated event should be avoided, because they are not maintained anymore", resolution: "Refer to the API of the element which event should be used instead.", resolutionurls: [{ text: "API Reference", href: "https://sapui5.hana.ondemand.com/#/api/deprecated" }], check: function(oIssueManager, oCoreFacade, oScope) { oScope.getElementsByClassName(Element).forEach(function(oElement) { var oMetadata = oElement.getMetadata(), mEvents = oMetadata.getAllEvents(); for (var sEvent in mEvents) { // if event is deprecated and developer added event handler if (mEvents[sEvent].deprecated && oElement.mEventRegistry[sEvent] && oElement.mEventRegistry[sEvent].length > 0) { oIssueManager.addIssue({ severity: Severity.Medium, details: "Deprecated event '" + sEvent + "' is used for element '" + oElement.getId() + "'.", context: { id: oElement.getId() } }); } } }); } }; return [ oXMLViewWrongNamespace, oXMLViewDefaultNamespace, oXMLViewLowerCaseControl, oXMLViewUnusedNamespaces, oDeprecatedPropertyRule, oDeprecatedAggregationRule, oDeprecatedAssociationRule, oDeprecatedEventRule ]; }, true);