Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Detect deprecated 'type' in View.create / <mvc:View> #475

Merged
merged 1 commit into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/linter/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export enum MESSAGE {
PARTIALLY_DEPRECATED_ODATA_MODEL_V2_CREATE_ENTRY,
PARTIALLY_DEPRECATED_ODATA_MODEL_V2_CREATE_ENTRY_PROPERTIES_ARRAY,
PARTIALLY_DEPRECATED_PARAMETERS_GET,
PARTIALLY_DEPRECATED_VIEW_CREATE,
PREFER_TEST_STARTER,
REDUNDANT_BOOTSTRAP_PARAM,
REDUNDANT_BOOTSTRAP_PARAM_ERROR,
Expand Down Expand Up @@ -482,6 +483,15 @@ export const MESSAGE_INFO = {
details: () => `{@link sap.ui.core.theming.Parameters#sap.ui.core.theming.Parameters.get Parameters.get}`,
},

[MESSAGE.PARTIALLY_DEPRECATED_VIEW_CREATE]: {
severity: LintMessageSeverity.Error,
ruleId: RULES["no-deprecated-api"],

message: ({typeValue}: {typeValue: string}) =>
`Usage of deprecated value '${typeValue}' for parameter 'type' in 'sap/ui/core/mvc/View.create'`,
details: () => `{@link sap.ui.core.mvc.View#sap.ui.core.mvc.View.create View.create}`,
},

[MESSAGE.REDUNDANT_BOOTSTRAP_PARAM]: {
severity: LintMessageSeverity.Warning,
ruleId: RULES["no-deprecated-api"],
Expand Down
40 changes: 40 additions & 0 deletions src/linter/ui5Types/SourceFileLinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const log = getLogger("linter:ui5Types:SourceFileLinter");
// https://github.com/SAP/openui5/blob/32c21c33d9dc29a32bf7ee7f41d7bae23dcf086b/src/sap.ui.core/src/sap/ui/test/starter/_utils.js#L287
const VALID_TESTSUITE = /^\/testsuite(?:\.[a-z][a-z0-9-]*)*\.qunit\.(?:js|ts)$/;

const DEPRECATED_VIEW_TYPES = ["JS", "JSON", "HTML", "Template"];

interface DeprecationInfo {
symbol: ts.Symbol;
messageDetails: string;
Expand Down Expand Up @@ -809,6 +811,8 @@ export default class SourceFileLinter {
this.#analyzeMobileInit(node);
} else if (symbolName === "setTheme" && moduleName === "sap/ui/core/Theming") {
this.#analyzeThemingSetTheme(node);
} else if (symbolName === "create" && moduleName === "sap/ui/core/mvc/View") {
this.#analyzeViewCreate(node);
} else if (/\.qunit\.(js|ts)$/.test(this.sourceFile.fileName) &&
symbolName === "ready" && moduleName === "sap/ui/core/Core") {
this.#reportTestStarter(node);
Expand Down Expand Up @@ -1123,6 +1127,42 @@ export default class SourceFileLinter {
}
}

#analyzeViewCreate(node: ts.CallExpression) {
if (!node.arguments?.length) {
return;
}

const optionsArg = node.arguments[0];

if (!ts.isObjectLiteralExpression(optionsArg)) {
return;
}

// Find "type" property
let typeProperty;
for (const property of optionsArg.properties) {
if (!ts.isPropertyAssignment(property)) {
continue;
}
if (
(ts.isIdentifier(property.name) || ts.isStringLiteral(property.name)) &&
property.name.text === "type"
) {
typeProperty = property;
break;
}
}

if (typeProperty && ts.isStringLiteralLike(typeProperty.initializer)) {
const typeValue = typeProperty.initializer.text;
if (DEPRECATED_VIEW_TYPES.includes(typeValue)) {
this.#reporter.addMessage(MESSAGE.PARTIALLY_DEPRECATED_VIEW_CREATE, {
typeValue,
}, node);
}
}
}

getDeprecationInfoForAccess(node: ts.AccessExpression): DeprecationInfo | null {
let symbol;
if (ts.isPropertyAccessExpression(node)) {
Expand Down
15 changes: 13 additions & 2 deletions src/linter/xmlTemplate/generator/AbstractGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default abstract class AbstractGenerator {
throw new Error("Not implemented");
}

writeControl(controlDeclaration: ControlDeclaration) {
writeControl(controlDeclaration: ControlDeclaration, rootControl = false) {
const importVariableName = this._getUniqueVariableName(controlDeclaration.name);

const moduleName = controlDeclaration.namespace.replaceAll(".", "/") + `/${controlDeclaration.name}`;
Expand All @@ -49,7 +49,18 @@ export default abstract class AbstractGenerator {

// Create the control
this._body.write(`const ${controlDeclaration.variableName} = `);
this._body.writeln(`new ${importVariableName}({`, controlDeclaration.start, controlDeclaration.end);

// Special case: Use View.create for nested views
if (!rootControl && moduleName === "sap/ui/core/mvc/View") {
this._body.writeln(
`await ${importVariableName}.create({`, controlDeclaration.start, controlDeclaration.end
);
} else {
this._body.writeln(
`new ${importVariableName}({`, controlDeclaration.start, controlDeclaration.end
);
}

// Write properties
controlDeclaration.properties.forEach((attribute) => {
// Add mapping of id to module name so that specific byId lookup for controllers can be generated.
Expand Down
2 changes: 1 addition & 1 deletion src/linter/xmlTemplate/generator/FragmentGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default class FragmentGenerator extends AbstractGenerator {
let returnVal;

if (controlInfo.kind === NodeKind.Control) {
this.writeControl(controlInfo);
this.writeControl(controlInfo, true);
returnVal = controlInfo.variableName;
} else if (controlInfo.kind === NodeKind.FragmentDefinition) {
const variables = Array.from(controlInfo.controls.values()).map((control) => {
Expand Down
2 changes: 1 addition & 1 deletion src/linter/xmlTemplate/generator/ViewGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export default class ViewGenerator extends AbstractGenerator {
}
});
this._body.write(`export default `);
this.writeControl(controlInfo);
this.writeControl(controlInfo, true);
}
}
56 changes: 54 additions & 2 deletions test/fixtures/linter/rules/NoDeprecatedApi/PartialDeprecation.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ sap.ui.define([
"sap/ui/model/odata/v2/ODataModel",
"sap/ui/core/Component",
"sap/ui/core/routing/Router",
"sap/ui/util/Mobile"
], function(Parameters, JSONModel, ODataModelV4, ODataModelV2, Component, Router, Mobile) {
"sap/ui/util/Mobile",
"sap/ui/core/mvc/View",
"sap/ui/core/mvc/ViewType"
], function(Parameters, JSONModel, ODataModelV4, ODataModelV2, Component, Router, Mobile, View, ViewType) {

Parameters.get(); // (deprecated since 1.92) If no parameter is given
Parameters.get("sapUiParam1"); // (deprecated since 1.94) If a string is given as first parameter
Expand Down Expand Up @@ -75,4 +77,54 @@ sap.ui.define([
homeIconPrecomposed: true, // Deprecated
});
Mobile.init({}); // Negative test: No deprecated parameters

View.create({
type: "JS", // Deprecated type
viewName: "myapp.view.Home"
});
View.create({
type: ViewType.JS, // Deprecated type
viewName: "myapp.view.Home"
});

View.create({
type: "JSON", // Deprecated type
viewName: "myapp.view.Home"
});
View.create({
type: ViewType.JSON, // Deprecated type
viewName: "myapp.view.Home"
});

View.create({
type: "HTML", // Deprecated type
viewName: "myapp.view.Home"
});
View.create({
type: ViewType.HTML, // Deprecated type
viewName: "myapp.view.Home"
});

View.create({
type: "Template", // Deprecated type
viewName: "myapp.view.Home"
});
View.create({
type: ViewType.Template, // Deprecated type
viewName: "myapp.view.Home"
});

// Negative tests: No deprecated types
View.create({
viewName: "module:myapp.view.Home"
});
View.create({
type: "XML",
viewName: "myapp.view.Home"
});
View.create({
type: ViewType.XML,
viewName: "myapp.view.Home"
});

});
14 changes: 14 additions & 0 deletions test/fixtures/linter/rules/NoDeprecatedApi/XMLView.view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,18 @@
<Button tap=".onButtonTap"/> <!-- Event "tap" is deprecated -->
</SegmentedButton>

<mvc:View type="JS" viewName="myapp.view.Home"/>
<mvc:View type="JSON" viewName="myapp.view.Home"/>
<mvc:View type="HTML" viewName="myapp.view.Home"/>
<mvc:View type="Template" viewName="myapp.view.Home"/>

<mvc:JSView viewName="myapp.view.Home"/>
<mvc:JSONView viewName="myapp.view.Home"/>
<mvc:HTMLView viewName="myapp.view.Home"/>
<mvc:TemplateView viewName="myapp.view.Home"/>

<!-- Negative test: XML view -->
<mvc:View type="XML" viewName="myapp.view.Home"/>
<mvc:XMLView viewName="myapp.view.Home"/>

</mvc:View>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<core:FragmentDefinition xmlns:core="sap.ui.core"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc"
>

<mvc:View type="XML" viewName="com.myapp.view.Home"/>
<mvc:View type="JS" viewName="com.myapp.view.Home"/>
<mvc:View type="JSON" viewName="com.myapp.view.Home"/>
<mvc:View type="HTML" viewName="com.myapp.view.Home"/>
<mvc:View type="Template" viewName="com.myapp.view.Home"/>

<mvc:XMLView viewName="com.myapp.view.Home"/>
<mvc:JSView viewName="com.myapp.view.Home"/>
<mvc:JSONView viewName="com.myapp.view.Home"/>
<mvc:HTMLView viewName="com.myapp.view.Home"/>
<mvc:TemplateView viewName="com.myapp.view.Home"/>

</core:FragmentDefinition>
17 changes: 17 additions & 0 deletions test/fixtures/transpiler/xml/XMLFragmentNestedViews.fragment.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<VBox xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc"
>

<mvc:View type="XML" viewName="com.myapp.view.Home"/>
<mvc:View type="JS" viewName="com.myapp.view.Home"/>
<mvc:View type="JSON" viewName="com.myapp.view.Home"/>
<mvc:View type="HTML" viewName="com.myapp.view.Home"/>
<mvc:View type="Template" viewName="com.myapp.view.Home"/>

<mvc:XMLView viewName="com.myapp.view.Home"/>
<mvc:JSView viewName="com.myapp.view.Home"/>
<mvc:JSONView viewName="com.myapp.view.Home"/>
<mvc:HTMLView viewName="com.myapp.view.Home"/>
<mvc:TemplateView viewName="com.myapp.view.Home"/>

</VBox>
18 changes: 18 additions & 0 deletions test/fixtures/transpiler/xml/XMLViewNestedViews.view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<mvc:View xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
controllerName="com.myapp.controller.Main"
>

<mvc:View type="XML" viewName="com.myapp.view.Home"/>
<mvc:View type="JS" viewName="com.myapp.view.Home"/>
<mvc:View type="JSON" viewName="com.myapp.view.Home"/>
<mvc:View type="HTML" viewName="com.myapp.view.Home"/>
<mvc:View type="Template" viewName="com.myapp.view.Home"/>

<mvc:XMLView viewName="com.myapp.view.Home"/>
<mvc:JSView viewName="com.myapp.view.Home"/>
<mvc:JSONView viewName="com.myapp.view.Home"/>
<mvc:HTMLView viewName="com.myapp.view.Home"/>
<mvc:TemplateView viewName="com.myapp.view.Home"/>

</mvc:View>
Loading
Loading