Skip to content

Commit

Permalink
feat: Detect deprecations in ui5.yaml (in root directory) (#39)
Browse files Browse the repository at this point in the history
- feat: Detect deprecated libs in any ui5.yaml
- deps: Add data-with-position npm library for YAML-parsing
(https://www.npmjs.com/package/data-with-position)
- refactor: Outsource list of deprecated libs to common utils constant

JIRA: CPOUI5FOUNDATION-822
  • Loading branch information
maxreichmann authored Apr 17, 2024
1 parent 508d81d commit db118b1
Show file tree
Hide file tree
Showing 20 changed files with 832 additions and 229 deletions.
423 changes: 217 additions & 206 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"@ui5/logger": "^3.0.0",
"@ui5/project": "^3.9.1",
"chalk": "^5.3.0",
"data-with-position": "^0.5.0",
"figures": "^6.1.0",
"he": "^1.2.0",
"json-source-map": "^0.6.1",
Expand Down
2 changes: 2 additions & 0 deletions src/linter/lintWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {AbstractAdapter} from "@ui5/fs";
import lintXml from "./xmlTemplate/linter.js";
import lintJson from "./manifestJson/linter.js";
import lintHtml from "./html/linter.js";
import lintUI5Yaml from "./yaml/linter.js";
import {taskStart} from "../util/perf.js";
import TypeLinter from "./ui5Types/TypeLinter.js";
import LinterContext, {LintResult, LinterParameters, LinterOptions} from "./LinterContext.js";
Expand All @@ -20,6 +21,7 @@ export default async function lintWorkspace(
lintXml(params),
lintJson(params),
lintHtml(params),
lintUI5Yaml(params),
]);

const typeLinter = new TypeLinter(params);
Expand Down
2 changes: 2 additions & 0 deletions src/linter/linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ function transformVirtualPathToFilePath(
return path.join(srcFsBasePath, posixPath.relative(srcVirBasePath, virtualPath));
} else if (testFsBasePath && testVirBasePath && virtualPath.startsWith(testVirBasePath)) {
return path.join(testFsBasePath, posixPath.relative(testVirBasePath, virtualPath));
} else if (virtualPath.startsWith("/")) {
return posixPath.relative("/", virtualPath);
} else {
throw new Error(
`Resource path ${virtualPath} is not located within the virtual source or test directories of the project`);
Expand Down
22 changes: 1 addition & 21 deletions src/linter/manifestJson/ManifestLinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,14 @@ import ManifestReporter from "./ManifestReporter.js";
import {LintMessageSeverity, ResourcePath} from "../LinterContext.js";
import jsonMap from "json-source-map";
import LinterContext from "../LinterContext.js";
import deprecatedLibraries from "../../utils/deprecatedLibs.js";

interface locType {
line: number;
column: number;
pos: number;
}

const deprecatedLibraries: string[] = [
"sap.ca.scfld.md",
"sap.ca.ui",
"sap.fe.common", // Internal, removed in 1.110
"sap.fe.plugins", // Internal, removed in 1.102
"sap.fe.semantics", // Internal, removed in 1.104
"sap.landvisz", // Removed in 1.120
"sap.makit",
"sap.me",
"sap.sac.grid", // Removed in 1.114
"sap.ui.commons",
"sap.ui.suite",
"sap.ui.ux3",
"sap.ui.vtm",
"sap.uiext.inbox",
"sap.webanalytics.core",
"sap.zen.commons",
"sap.zen.crosstab",
"sap.zen.dsh",
];

const deprecatedComponents: string[] = [
"sap.zen.dsh.fioriwrapper",
];
Expand Down
83 changes: 83 additions & 0 deletions src/linter/yaml/YamlLinter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {LintMessageSeverity} from "../LinterContext.js";
import LinterContext from "../LinterContext.js";
import deprecatedLibraries from "../../utils/deprecatedLibs.js";
import {DataWithPosition, fromYaml, getPosition} from "data-with-position";

interface YamlWithPosInfo extends DataWithPosition {
framework?: {
libraries?: {
name: string;
}[];
};
positionKey?: {
end: {
column: number;
line: number;
};
start: {
column: number;
line: number;
};
};
}

export default class YamlLinter {
#content;
#resourcePath;
#context: LinterContext;

constructor(resourcePath: string, content: string, context: LinterContext) {
this.#content = content;
this.#resourcePath = resourcePath;
this.#context = context;
}

// eslint-disable-next-line @typescript-eslint/require-await
async lint() {
try {
// Split Yaml file into part documents by '---' separator
const allDocuments: string[] = this.#content.split(/(?:\r\n|\r|\n)---/g);

// Calculate the starting line number of each part document
let lineNumberOffset = 0;
allDocuments.forEach((document: string) => {
// Parse content only of the current part
const parsedYamlWithPosInfo: YamlWithPosInfo = this.#parseYaml(document);
// Analyze part content with line number offset
this.#analyzeYaml(parsedYamlWithPosInfo, lineNumberOffset);
// Update line number offset for next part
lineNumberOffset += document.split(/\r\n|\r|\n/g).length;
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
this.#context.addLintingMessage(this.#resourcePath, {
severity: LintMessageSeverity.Error,
message,
ruleId: "ui5-linter-parsing-error",
fatal: true,
});
}
}

#parseYaml(content: string): YamlWithPosInfo {
// Create JS object from YAML content with position information
return fromYaml(content) as YamlWithPosInfo;
}

#analyzeYaml(yaml: YamlWithPosInfo, offset: number) {
// Check for deprecated libraries
yaml?.framework?.libraries?.forEach((lib) => {
if (deprecatedLibraries.includes(lib.name.toString())) {
const positionInfo = getPosition(lib);
this.#context.addLintingMessage(this.#resourcePath, {
ruleId: "ui5-linter-no-deprecated-api",
severity: LintMessageSeverity.Error,
fatal: undefined,
line: positionInfo.start.line + offset,
column: positionInfo.start.column,
message: `Use of deprecated library '${lib.name}'`,
});
}
});
}
}
29 changes: 29 additions & 0 deletions src/linter/yaml/linter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {LinterParameters} from "../LinterContext.js";
import YamlLinter from "./YamlLinter.js";
import {Resource} from "@ui5/fs";

export default async function lintUI5Yaml({context}: LinterParameters) {
let ui5YamlResources: Resource[];
const pathsToLint = context.getPathsToLint();
const reader = context.getRootReader();
if (pathsToLint?.length) {
ui5YamlResources = [];
await Promise.all(pathsToLint.map(async (resourcePath) => {
if (!resourcePath.endsWith(".yaml")) {
return;
}
const resource = await reader.byPath(resourcePath);
if (!resource) {
throw new Error(`Resource not found: ${resourcePath}`);
}
ui5YamlResources.push(resource);
}));
} else {
ui5YamlResources = await reader.byGlob("/{ui5.yaml,*-ui5.yaml,*.ui5.yaml,ui5-*.yaml}");
}

await Promise.all(ui5YamlResources.map(async (resource: Resource) => {
const linter = new YamlLinter(resource.getPath(), await resource.getString(), context);
await linter.lint();
}));
}
1 change: 1 addition & 0 deletions src/untyped.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ declare module "@ui5/project" {
interface Project {
getNamespace: () => ProjectNamespace;
getReader: (options: import("@ui5/fs").ReaderOptions) => import("@ui5/fs").AbstractReader;
getRootReader: () => import("@ui5/fs").AbstractReader;
getRootPath: () => string;
getSourcePath: () => string;
_testPath: string; // TODO UI5 Tooling: Expose API for optional test path
Expand Down
22 changes: 22 additions & 0 deletions src/utils/deprecatedLibs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const deprecatedLibs: string[] = [
"sap.ca.scfld.md",
"sap.ca.ui",
"sap.fe.common", // Internal, removed in 1.110
"sap.fe.plugins", // Internal, removed in 1.102
"sap.fe.semantics", // Internal, removed in 1.104
"sap.landvisz", // Removed in 1.120
"sap.makit",
"sap.me",
"sap.sac.grid", // Removed in 1.114
"sap.ui.commons",
"sap.ui.suite",
"sap.ui.ux3",
"sap.ui.vtm",
"sap.uiext.inbox",
"sap.webanalytics.core",
"sap.zen.commons",
"sap.zen.crosstab",
"sap.zen.dsh",
];

export default deprecatedLibs;
11 changes: 11 additions & 0 deletions test/fixtures/linter/projects/com.ui5.troublesome.app/ui5.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
specVersion: '3.0'
metadata:
name: com.ui5.troublesome.app
type: application
framework:
name: OpenUI5
version: "1.121.0"
libraries:
- name: sap.m
- name: sap.ui.core
- name: sap.landvisz
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ framework:
version: "1.120.6"
libraries:
- name: sap.ui.core
- name: sap.landvisz
11 changes: 11 additions & 0 deletions test/fixtures/linter/rules/NoDeprecatedApi/ui5.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
specVersion: '3.0'
metadata:
name: com.ui5.troublesome.app
type: application
framework:
name: OpenUI5
version: "1.121.0"
libraries:
- name: sap.m
- name: sap.ui.core
- name: sap.landvisz
Binary file not shown.
Loading

0 comments on commit db118b1

Please sign in to comment.