diff --git a/package-lock.json b/package-lock.json index 1c4e69152..47e089906 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,8 +16,10 @@ "@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", + "js-yaml": "^4.1.0", "json-source-map": "^0.6.1", "sax-wasm": "^2.2.4", "typescript": "5.3.x", @@ -35,6 +37,7 @@ "@istanbuljs/nyc-config-typescript": "^1.0.2", "@stylistic/eslint-plugin": "^1.7.0", "@types/he": "^1.2.3", + "@types/js-yaml": "^4.0.9", "@types/node": "^20.12.5", "@types/sinon": "^17.0.3", "@types/update-notifier": "^6.0.8", @@ -1127,12 +1130,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1158,18 +1155,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1345,6 +1330,15 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -1358,6 +1352,19 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -2766,6 +2773,12 @@ "@types/sizzle": "*" } }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -3576,11 +3589,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@ui5/project/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/@ui5/project/node_modules/escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", @@ -3610,17 +3618,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@ui5/project/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@ui5/project/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3886,13 +3883,9 @@ } }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-differ": { "version": "3.0.0", @@ -5098,24 +5091,6 @@ "typescript": ">=4" } }, - "node_modules/cosmiconfig/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -5216,6 +5191,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/data-with-position": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/data-with-position/-/data-with-position-0.5.0.tgz", + "integrity": "sha512-GhsgEIPWk7WCAisjwBkOjvPqpAlVUOSl1CTmy9KyhVMG1wxl29Zj5+J71WhQ/KgoJS/Psxq6Cnioz3xdBjeIWQ==", + "dependencies": { + "yaml-ast-parser": "^0.0.43" + } + }, "node_modules/date-time": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", @@ -5393,6 +5376,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/depcheck/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/depcheck/node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -5450,6 +5442,19 @@ "node": ">=10" } }, + "node_modules/depcheck/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/depcheck/node_modules/minimatch": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", @@ -5494,6 +5499,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/depcheck/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/depcheck/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -5934,12 +5948,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -6027,18 +6035,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -7708,13 +7704,11 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -8296,11 +8290,6 @@ "markdown-it": "*" } }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/markdown-it/node_modules/entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", @@ -12076,6 +12065,28 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/supertap/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/supertap/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12940,14 +12951,10 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==" }, "node_modules/yargs": { "version": "17.7.2", diff --git a/package.json b/package.json index 01c8e572d..342031786 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,10 @@ "@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", + "js-yaml": "^4.1.0", "json-source-map": "^0.6.1", "sax-wasm": "^2.2.4", "typescript": "5.3.x", @@ -82,6 +84,7 @@ "@istanbuljs/nyc-config-typescript": "^1.0.2", "@stylistic/eslint-plugin": "^1.7.0", "@types/he": "^1.2.3", + "@types/js-yaml": "^4.0.9", "@types/node": "^20.12.5", "@types/sinon": "^17.0.3", "@types/update-notifier": "^6.0.8", diff --git a/src/linter/lintWorkspace.ts b/src/linter/lintWorkspace.ts index 61decabeb..868db2c53 100644 --- a/src/linter/lintWorkspace.ts +++ b/src/linter/lintWorkspace.ts @@ -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"; @@ -20,6 +21,7 @@ export default async function lintWorkspace( lintXml(params), lintJson(params), lintHtml(params), + lintUI5Yaml(params), ]); const typeLinter = new TypeLinter(params); diff --git a/src/linter/yaml/UI5YamlLinter.ts b/src/linter/yaml/UI5YamlLinter.ts new file mode 100644 index 000000000..425a9efa2 --- /dev/null +++ b/src/linter/yaml/UI5YamlLinter.ts @@ -0,0 +1,87 @@ +import {LintMessageSeverity} from "../LinterContext.js"; +import LinterContext from "../LinterContext.js"; +import deprecatedLibraries from "../../utils/deprecatedLibs.js"; +import yaml from "js-yaml"; +import {DataWithPosition, fromYaml, getPosition} from "data-with-position"; + +// file content schema of 'UI5Yaml' with only relevant properties +interface UI5YamlContentSchema { // extend for further detections + framework: { + libraries: { + name: string; + }[]; + }; +} + +interface UI5YamlContentSchemaWithPosInfo extends DataWithPosition { + framework?: { + libraries?: { + name: string; + }[]; + }; + positionKey?: { + end: { + column: number; + line: number; + }; + start: { + column: number; + line: number; + }; + }; +} + +export default class UI5YamlLinter { + #content = ""; + #yamlContentWithPosInfo: UI5YamlContentSchemaWithPosInfo = {}; + #resourcePath = ""; + #context: LinterContext; + + constructor(content: string, resourcePath: string, context: LinterContext) { + this.#content = content; + this.#resourcePath = resourcePath; + this.#context = context; + } + + // eslint-disable-next-line @typescript-eslint/require-await + async lint() { + try { + const source: UI5YamlContentSchema = this.#parseUI5Yaml(this.#content); + this.#analyzeUI5Yaml(source); + } 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, + }); + } + } + + #parseUI5Yaml(fileContent: string): UI5YamlContentSchema { + // Create JS object from YAML content with position information + this.#yamlContentWithPosInfo = fromYaml(fileContent) as UI5YamlContentSchemaWithPosInfo; + // Convert YAML content to JS object + return yaml.load(fileContent) as UI5YamlContentSchema; + } + + #analyzeUI5Yaml(ui5YamlObject: UI5YamlContentSchema) { + // Check for deprecated libraries + if (ui5YamlObject?.framework?.libraries?.length) { + ui5YamlObject.framework.libraries.forEach((lib, index: number) => { + if (deprecatedLibraries.includes(lib.name)) { + const positionInfo = getPosition(this.#yamlContentWithPosInfo.framework!.libraries![index]); + this.#context.addLintingMessage(this.#resourcePath, { + ruleId: "ui5-linter-no-deprecated-api", + severity: LintMessageSeverity.Error, + fatal: undefined, + line: positionInfo.start.line, + column: positionInfo.start.column, + message: `Use of deprecated library '${lib.name}'`, + }); + } + }); + } + } +} diff --git a/src/linter/yaml/linter.ts b/src/linter/yaml/linter.ts new file mode 100644 index 000000000..805bf839e --- /dev/null +++ b/src/linter/yaml/linter.ts @@ -0,0 +1,29 @@ +import {LinterParameters} from "../LinterContext.js"; +import UI5YamlLinter from "./UI5YamlLinter.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 UI5YamlLinter(resource.getPath(), await resource.getString(), context); + await linter.lint(); + })); +} diff --git a/src/untyped.d.ts b/src/untyped.d.ts index 0e47ce5ad..368abc82e 100644 --- a/src/untyped.d.ts +++ b/src/untyped.d.ts @@ -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 diff --git a/src/utils/deprecatedLibs.ts b/src/utils/deprecatedLibs.ts new file mode 100644 index 000000000..3df8c10d8 --- /dev/null +++ b/src/utils/deprecatedLibs.ts @@ -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; diff --git a/test/fixtures/linter/projects/com.ui5.troublesome.app/ui5.yaml b/test/fixtures/linter/projects/com.ui5.troublesome.app/ui5.yaml new file mode 100644 index 000000000..dd924281f --- /dev/null +++ b/test/fixtures/linter/projects/com.ui5.troublesome.app/ui5.yaml @@ -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 diff --git a/test/fixtures/linter/projects/library.with.custom.paths/ui5.yaml b/test/fixtures/linter/projects/library.with.custom.paths/ui5.yaml index ff8a65e6d..e092fab59 100644 --- a/test/fixtures/linter/projects/library.with.custom.paths/ui5.yaml +++ b/test/fixtures/linter/projects/library.with.custom.paths/ui5.yaml @@ -12,3 +12,4 @@ framework: version: "1.120.6" libraries: - name: sap.ui.core + - name: sap.landvisz diff --git a/test/fixtures/linter/rules/NoDeprecatedApi/ui5.yaml b/test/fixtures/linter/rules/NoDeprecatedApi/ui5.yaml new file mode 100644 index 000000000..dd924281f --- /dev/null +++ b/test/fixtures/linter/rules/NoDeprecatedApi/ui5.yaml @@ -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 diff --git a/test/lib/detectors/transpilers/xml/snapshots/transpiler.ts.snap b/test/lib/detectors/transpilers/xml/snapshots/transpiler.ts.snap new file mode 100644 index 000000000..33cb3f6fc Binary files /dev/null and b/test/lib/detectors/transpilers/xml/snapshots/transpiler.ts.snap differ diff --git a/test/lib/linter/UI5YamlLinter.ts b/test/lib/linter/UI5YamlLinter.ts new file mode 100644 index 000000000..186b65396 --- /dev/null +++ b/test/lib/linter/UI5YamlLinter.ts @@ -0,0 +1,52 @@ +import test from "ava"; +import UI5YamlLinter from "../../../src/linter/yaml/UI5YamlLinter.js"; +import LinterContext from "../../../src/linter/LinterContext.js"; + +test("Test UI5YamlLinter report (parsing and analyzing)", async (t) => { + /* Mock resource content of ui5.yaml file, + (formatted as used in src/detectors/typeChecker/index.ts - #analyzeFiles()), + (contains relevant 'framework' property and 'libraries' sub-property), + (contains only deprecated libraries) */ + const resourceContent = +`specVersion: '3.0' +metadata: + name: ava-test-ui5yamllinter +type: application +framework: + name: OpenUI5 + version: "1.121.0" + libraries: + - name: sap.ca.scfld.md + - name: sap.ca.ui + - name: sap.fe.common`; + + const resourcePath = "/ui5.js"; // '.js' due to renaming in src/detectors/typeChecker/index.ts - #analyzeFiles() + const projectPath = "test.ui5yamllinter"; + const context = new LinterContext({rootDir: projectPath}); + + // Create UI5YamlLinter instance with resource content + const linter = new UI5YamlLinter(resourceContent, resourcePath, context); + // Run UI5YamlLinter report + await linter.lint(); + + const messages = context.getLintingMessages("/ui5.js"); + + // Test returned messages + t.is(messages.length, 3, "Detection of 3 deprecated libraries expected"); + + // Test each message + t.is(messages[0].ruleId, "ui5-linter-no-deprecated-api", `RuleId is correct`); + t.is(messages[0].message, `Use of deprecated library 'sap.ca.scfld.md'`, `Message is correct`); + t.is(messages[0].column, 7, `Column is correct`); + t.is(messages[0].line, 9, `Line is correct`); + + t.is(messages[1].ruleId, "ui5-linter-no-deprecated-api", `RuleId is correct`); + t.is(messages[1].message, `Use of deprecated library 'sap.ca.ui'`, `Message is correct`); + t.is(messages[1].column, 7, `Column is correct`); + t.is(messages[1].line, 10, `Line is correct`); + + t.is(messages[2].ruleId, "ui5-linter-no-deprecated-api", `RuleId is correct`); + t.is(messages[2].message, `Use of deprecated library 'sap.fe.common'`, `Message is correct`); + t.is(messages[2].column, 7, `Column is correct`); + t.is(messages[2].line, 11, `Line is correct`); +}); diff --git a/test/lib/linter/_linterHelper.ts b/test/lib/linter/_linterHelper.ts index 60d21dce0..9050b5385 100644 --- a/test/lib/linter/_linterHelper.ts +++ b/test/lib/linter/_linterHelper.ts @@ -70,8 +70,9 @@ export function createTestsForFixtures(fixturesPath: string) { if (!fileName.endsWith(".js") && !fileName.endsWith(".xml") && !fileName.endsWith(".json") && - !fileName.endsWith(".html")) { - // Ignore non-JavaScript, non-XML, non-JSON and non-HTML files + !fileName.endsWith(".html") && + !fileName.endsWith(".yaml")) { + // Ignore non-JavaScript, non-XML, non-JSON, non-HTML and non-YAML files continue; } let testName = fileName; diff --git a/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.md b/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.md index db9f30fc8..3e227b2f5 100644 --- a/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.md +++ b/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.md @@ -1222,3 +1222,9 @@ Generated by [AVA](https://avajs.dev). warningCount: 0, }, ] + +## General: ui5.yaml + +> Snapshot 1 + + [] diff --git a/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.snap b/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.snap index bcb2e27af..6a36cac2d 100644 Binary files a/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.snap and b/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.snap differ diff --git a/test/lib/linter/snapshots/linter.ts.md b/test/lib/linter/snapshots/linter.ts.md index ab8fd7ad9..2b477c520 100644 --- a/test/lib/linter/snapshots/linter.ts.md +++ b/test/lib/linter/snapshots/linter.ts.md @@ -266,6 +266,23 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 [ + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'ui5.yaml', + messages: [ + { + column: 7, + fatal: undefined, + line: 11, + message: 'Use of deprecated library \'sap.landvisz\'', + ruleId: 'ui5-linter-no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, { coverageInfo: [], errorCount: 0, @@ -972,6 +989,23 @@ Generated by [AVA](https://avajs.dev). ], warningCount: 0, }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'ui5.yaml', + messages: [ + { + column: 7, + fatal: undefined, + line: 15, + message: 'Use of deprecated library \'sap.landvisz\'', + ruleId: 'ui5-linter-no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, ] ## lint: All files of library with sap.f namespace diff --git a/test/lib/utils/deprecatedLibs.ts b/test/lib/utils/deprecatedLibs.ts new file mode 100644 index 000000000..036833577 --- /dev/null +++ b/test/lib/utils/deprecatedLibs.ts @@ -0,0 +1,27 @@ +import test from "ava"; +import deprecatedLibs from "../../../src/utils/deprecatedLibs.js"; + +test("Test Deprecated Libs constant", (t) => { + const expectedDeprecatedLibs: 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", + ]; + t.deepEqual(deprecatedLibs, expectedDeprecatedLibs, + "Expected deprecated libraries list should match the actual list."); +});