From 20bd0c35848571eb9918a4da03c7bf4e2e4e4143 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 11 Dec 2025 15:46:50 +0100 Subject: [PATCH 1/6] fix(project): Allow Component.ts --- .../lib/specifications/types/Component.js | 7 +++++-- .../src-with-component-js/Component.js | 8 ++++++++ .../src-with-component-js/manifest.json | 13 +++++++++++++ .../src-with-component-ts/Component.ts | 7 +++++++ .../src-with-component-ts/manifest.json | 13 +++++++++++++ .../test/lib/specifications/types/Component.js | 17 ++++++++++++++--- 6 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 packages/project/test/fixtures/component.h/src-with-component-js/Component.js create mode 100644 packages/project/test/fixtures/component.h/src-with-component-js/manifest.json create mode 100644 packages/project/test/fixtures/component.h/src-with-component-ts/Component.ts create mode 100644 packages/project/test/fixtures/component.h/src-with-component-ts/manifest.json diff --git a/packages/project/lib/specifications/types/Component.js b/packages/project/lib/specifications/types/Component.js index fbf77f94ec8..71229e79027 100644 --- a/packages/project/lib/specifications/types/Component.js +++ b/packages/project/lib/specifications/types/Component.js @@ -292,10 +292,13 @@ class Component extends ComponentProject { async _ensureComponent() { // Ensure that a Component.js exists - const componentResource = await this._getRawSourceReader().byPath("/Component.js"); + const componentResource = await this._getRawSourceReader().byPath("/Component.js") || + await this._getRawSourceReader().byPath("/Component.ts"); if (!componentResource) { throw new Error( - `Unable to find required file Component.js in component project ${this.getName()}`); + `Unable to find either required "Component.js" or "Component.ts"` + + ` in component project ${this.getName()}` + ); } } } diff --git a/packages/project/test/fixtures/component.h/src-with-component-js/Component.js b/packages/project/test/fixtures/component.h/src-with-component-js/Component.js new file mode 100644 index 00000000000..cb9bd406864 --- /dev/null +++ b/packages/project/test/fixtures/component.h/src-with-component-js/Component.js @@ -0,0 +1,8 @@ +sap.ui.define(["sap/ui/core/UIComponent"], function(UIComponent){ + "use strict"; + return UIComponent.extend('application.h.Component', { + metadata: { + manifest: "json" + } + }); +}); diff --git a/packages/project/test/fixtures/component.h/src-with-component-js/manifest.json b/packages/project/test/fixtures/component.h/src-with-component-js/manifest.json new file mode 100644 index 00000000000..7d63e359cdf --- /dev/null +++ b/packages/project/test/fixtures/component.h/src-with-component-js/manifest.json @@ -0,0 +1,13 @@ +{ + "_version": "1.1.0", + "sap.app": { + "_version": "1.1.0", + "id": "${componentName}", + "type": "application", + "applicationVersion": { + "version": "1.2.2" + }, + "embeds": ["embedded"], + "title": "{{title}}" + } +} diff --git a/packages/project/test/fixtures/component.h/src-with-component-ts/Component.ts b/packages/project/test/fixtures/component.h/src-with-component-ts/Component.ts new file mode 100644 index 00000000000..987db02269f --- /dev/null +++ b/packages/project/test/fixtures/component.h/src-with-component-ts/Component.ts @@ -0,0 +1,7 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +export default class Component extends UIComponent { + public static metadata = { + "manifest": "json" + }; +}; diff --git a/packages/project/test/fixtures/component.h/src-with-component-ts/manifest.json b/packages/project/test/fixtures/component.h/src-with-component-ts/manifest.json new file mode 100644 index 00000000000..7d63e359cdf --- /dev/null +++ b/packages/project/test/fixtures/component.h/src-with-component-ts/manifest.json @@ -0,0 +1,13 @@ +{ + "_version": "1.1.0", + "sap.app": { + "_version": "1.1.0", + "id": "${componentName}", + "type": "application", + "applicationVersion": { + "version": "1.2.2" + }, + "embeds": ["embedded"], + "title": "{{title}}" + } +} diff --git a/packages/project/test/lib/specifications/types/Component.js b/packages/project/test/lib/specifications/types/Component.js index fec0ae0c744..78ffabc1680 100644 --- a/packages/project/test/lib/specifications/types/Component.js +++ b/packages/project/test/lib/specifications/types/Component.js @@ -678,11 +678,22 @@ test("namespace: detect namespace from pom.xml via ${appId} from properties", as " couldn't be resolved from maven property \"appId\" of pom.xml of project component.h"); }); -test("Throw for missing Component.js", async (t) => { +test("Throw when neither Component.js nor Component.ts exists", async (t) => { const {componentHInput} = t.context; componentHInput.configuration.resources.configuration.paths.src = "src-no-component"; - const error = await t.throwsAsync(Specification.create(componentHInput)); t.is(error.message, - "Unable to find required file Component.js in component project component.h"); + "Unable to find either required \"Component.js\" or \"Component.ts\" in component project component.h"); +}); + +test("Do not throw when Component.js exists", async (t) => { + const {componentHInput} = t.context; + componentHInput.configuration.resources.configuration.paths.src = "src-with-component-js"; + await t.notThrowsAsync(Specification.create(componentHInput), "Should not throw an error"); +}); + +test("Do not throw when Component.ts exists", async (t) => { + const {componentHInput} = t.context; + componentHInput.configuration.resources.configuration.paths.src = "src-with-component-ts"; + await t.notThrowsAsync(Specification.create(componentHInput), "Should not throw an error"); }); From 47cc3102a8ecb9e9b6ea55a94b622a50e5cd9458 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 11 Dec 2025 15:58:12 +0100 Subject: [PATCH 2/6] refactor(project): Add ignore for depcheck in package.json (false-positive due to Component.ts) --- packages/project/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/project/package.json b/packages/project/package.json index 572fa53b76e..e3080f49788 100644 --- a/packages/project/package.json +++ b/packages/project/package.json @@ -48,7 +48,7 @@ "version": "git-chglog --sort semver --next-tag v$npm_package_version -o CHANGELOG.md v4.0.0.. && git add CHANGELOG.md", "prepublishOnly": "git push --follow-tags", "release-note": "git-chglog --sort semver -c .chglog/release-config.yml v$npm_package_version", - "depcheck": "depcheck --ignores @ui5/project,@istanbuljs/esm-loader-hook,rimraf" + "depcheck": "depcheck --ignores @ui5/project,@istanbuljs/esm-loader-hook,rimraf,sap" }, "files": [ "CHANGELOG.md", From 85f7366ea0cf2beaaea8ee6e56ad404f553eafcf Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 11 Dec 2025 16:09:47 +0100 Subject: [PATCH 3/6] refactor: Fix comment --- packages/project/lib/specifications/types/Component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/project/lib/specifications/types/Component.js b/packages/project/lib/specifications/types/Component.js index 71229e79027..642e3455b96 100644 --- a/packages/project/lib/specifications/types/Component.js +++ b/packages/project/lib/specifications/types/Component.js @@ -291,7 +291,7 @@ class Component extends ComponentProject { } async _ensureComponent() { - // Ensure that a Component.js exists + // Ensure that a Component.js/ts exists const componentResource = await this._getRawSourceReader().byPath("/Component.js") || await this._getRawSourceReader().byPath("/Component.ts"); if (!componentResource) { From da6c909361652e8f842699d72310d0264938c8e8 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 11 Dec 2025 16:38:59 +0100 Subject: [PATCH 4/6] refactor: Throw if both Component.js & Component.js exist at the same time --- .../project/lib/specifications/types/Component.js | 15 +++++++++++---- .../Component.js | 8 ++++++++ .../Component.ts | 7 +++++++ .../manifest.json | 13 +++++++++++++ .../test/lib/specifications/types/Component.js | 8 ++++++++ 5 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/Component.js create mode 100644 packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/Component.ts create mode 100644 packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/manifest.json diff --git a/packages/project/lib/specifications/types/Component.js b/packages/project/lib/specifications/types/Component.js index 642e3455b96..a5754372470 100644 --- a/packages/project/lib/specifications/types/Component.js +++ b/packages/project/lib/specifications/types/Component.js @@ -291,10 +291,17 @@ class Component extends ComponentProject { } async _ensureComponent() { - // Ensure that a Component.js/ts exists - const componentResource = await this._getRawSourceReader().byPath("/Component.js") || - await this._getRawSourceReader().byPath("/Component.ts"); - if (!componentResource) { + // Throw if neither Component.js nor Component.ts is present + // or if both are present + const componentJSResource = await this._getRawSourceReader().byPath("/Component.js"); + const componentTSResource = await this._getRawSourceReader().byPath("/Component.ts"); + if (componentJSResource && componentTSResource) { + throw new Error( + `Both "Component.js" and "Component.ts" found` + + ` in component project ${this.getName()}` + ); + } + if (!componentJSResource && !componentTSResource) { throw new Error( `Unable to find either required "Component.js" or "Component.ts"` + ` in component project ${this.getName()}` diff --git a/packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/Component.js b/packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/Component.js new file mode 100644 index 00000000000..cb9bd406864 --- /dev/null +++ b/packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/Component.js @@ -0,0 +1,8 @@ +sap.ui.define(["sap/ui/core/UIComponent"], function(UIComponent){ + "use strict"; + return UIComponent.extend('application.h.Component', { + metadata: { + manifest: "json" + } + }); +}); diff --git a/packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/Component.ts b/packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/Component.ts new file mode 100644 index 00000000000..987db02269f --- /dev/null +++ b/packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/Component.ts @@ -0,0 +1,7 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +export default class Component extends UIComponent { + public static metadata = { + "manifest": "json" + }; +}; diff --git a/packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/manifest.json b/packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/manifest.json new file mode 100644 index 00000000000..7d63e359cdf --- /dev/null +++ b/packages/project/test/fixtures/component.h/src-with-component-js-and-component-ts/manifest.json @@ -0,0 +1,13 @@ +{ + "_version": "1.1.0", + "sap.app": { + "_version": "1.1.0", + "id": "${componentName}", + "type": "application", + "applicationVersion": { + "version": "1.2.2" + }, + "embeds": ["embedded"], + "title": "{{title}}" + } +} diff --git a/packages/project/test/lib/specifications/types/Component.js b/packages/project/test/lib/specifications/types/Component.js index 78ffabc1680..448e5829947 100644 --- a/packages/project/test/lib/specifications/types/Component.js +++ b/packages/project/test/lib/specifications/types/Component.js @@ -697,3 +697,11 @@ test("Do not throw when Component.ts exists", async (t) => { componentHInput.configuration.resources.configuration.paths.src = "src-with-component-ts"; await t.notThrowsAsync(Specification.create(componentHInput), "Should not throw an error"); }); + +test("Throw when both Component.js and Component.ts exist", async (t) => { + const {componentHInput} = t.context; + componentHInput.configuration.resources.configuration.paths.src = "src-with-component-js-and-component-ts"; + const error = await t.throwsAsync(Specification.create(componentHInput)); + t.is(error.message, + "Both \"Component.js\" and \"Component.ts\" found in component project component.h"); +}); From 1f661f31b5418ac905f33def390c38aa23523394 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 12 Dec 2025 14:15:11 +0100 Subject: [PATCH 5/6] refactor: Do not throw when both Component.js & Component.ts exist --> only throw when both are missing --- .../project/lib/specifications/types/Component.js | 12 +++--------- .../test/lib/specifications/types/Component.js | 6 ++---- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/packages/project/lib/specifications/types/Component.js b/packages/project/lib/specifications/types/Component.js index a5754372470..a514b5c110f 100644 --- a/packages/project/lib/specifications/types/Component.js +++ b/packages/project/lib/specifications/types/Component.js @@ -293,15 +293,9 @@ class Component extends ComponentProject { async _ensureComponent() { // Throw if neither Component.js nor Component.ts is present // or if both are present - const componentJSResource = await this._getRawSourceReader().byPath("/Component.js"); - const componentTSResource = await this._getRawSourceReader().byPath("/Component.ts"); - if (componentJSResource && componentTSResource) { - throw new Error( - `Both "Component.js" and "Component.ts" found` + - ` in component project ${this.getName()}` - ); - } - if (!componentJSResource && !componentTSResource) { + const componentResource = await this._getRawSourceReader().byPath("/Component.js") || + await this._getRawSourceReader().byPath("/Component.ts"); + if (!componentResource) { throw new Error( `Unable to find either required "Component.js" or "Component.ts"` + ` in component project ${this.getName()}` diff --git a/packages/project/test/lib/specifications/types/Component.js b/packages/project/test/lib/specifications/types/Component.js index 448e5829947..f5713be9d95 100644 --- a/packages/project/test/lib/specifications/types/Component.js +++ b/packages/project/test/lib/specifications/types/Component.js @@ -698,10 +698,8 @@ test("Do not throw when Component.ts exists", async (t) => { await t.notThrowsAsync(Specification.create(componentHInput), "Should not throw an error"); }); -test("Throw when both Component.js and Component.ts exist", async (t) => { +test("Do not throw when both Component.js and Component.ts exist", async (t) => { const {componentHInput} = t.context; componentHInput.configuration.resources.configuration.paths.src = "src-with-component-js-and-component-ts"; - const error = await t.throwsAsync(Specification.create(componentHInput)); - t.is(error.message, - "Both \"Component.js\" and \"Component.ts\" found in component project component.h"); + await t.notThrowsAsync(Specification.create(componentHInput), "Should not throw an error"); }); From 4e971cd5cfa11e4b319002c6d6c2f9c7d9264c05 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Mon, 15 Dec 2025 09:52:52 +0100 Subject: [PATCH 6/6] refactor: Adjust comment --- packages/project/lib/specifications/types/Component.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/project/lib/specifications/types/Component.js b/packages/project/lib/specifications/types/Component.js index a514b5c110f..8ca5b94df26 100644 --- a/packages/project/lib/specifications/types/Component.js +++ b/packages/project/lib/specifications/types/Component.js @@ -292,7 +292,6 @@ class Component extends ComponentProject { async _ensureComponent() { // Throw if neither Component.js nor Component.ts is present - // or if both are present const componentResource = await this._getRawSourceReader().byPath("/Component.js") || await this._getRawSourceReader().byPath("/Component.ts"); if (!componentResource) {