From 30ed97c05261bdea760c069f14fe1f79605a0957 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 2 Oct 2026 14:47:16 +0200 Subject: [PATCH 01/14] feat: Offer configPath property to workspace configs --- .../lib/validation/schema/ui5-workspace.json | 23 +++++++++++++++++-- packages/project/test/lib/graph/Workspace.js | 13 +++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/project/lib/validation/schema/ui5-workspace.json b/packages/project/lib/validation/schema/ui5-workspace.json index d4cd46c1946..f7a4bf76826 100644 --- a/packages/project/lib/validation/schema/ui5-workspace.json +++ b/packages/project/lib/validation/schema/ui5-workspace.json @@ -9,7 +9,7 @@ "additionalProperties": false, "properties": { "specVersion": { - "enum": ["workspace/1.0"], + "enum": ["workspace/1.1", "workspace/1.0"], "errorMessage": "Unsupported \"specVersion\"\nYour UI5 CLI installation might be outdated.\nSupported specification versions: \"workspace/1.0\"\nFor details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions" }, "metadata": { @@ -19,6 +19,23 @@ "$ref": "#/definitions/dependencyManagement" } }, + "if": { + "properties": {"specVersion": {"const": "workspace/1.0"}} + }, + "then": { + "properties": { + "dependencyManagement": { + "properties": { + "resolutions": { + "items": { + "not": {"required": ["configPath"]}, + "errorMessage": "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher" + } + } + } + } + } + }, "definitions": { "metadata": { "type": "object", @@ -42,13 +59,15 @@ "properties": { "resolutions": { "type": "array", - "items": { "type": "object", "additionalProperties": false, "properties": { "path": { "type": "string" + }, + "configPath": { + "type": "string" } } } diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 6e5b586e138..d7cc2e9399b 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -473,3 +473,16 @@ test("Missing parameters", (t) => { message: "Could not create Workspace: Missing or empty parameter 'configuration'" }, "Threw with expected error message"); }); + +test("Basic validation of workspace with specified configPath for dependency", (t) => { + // TODO: add test which passes the following yaml example: + // specVersion: workspace/1.1 + // metadata: + // name: default + // dependencyManagement: + // resolutions: + // - path: ../my-reuse-library + // configPath: ui5-mock.yaml + + +}); From d006aa8cece52c698ba5c0f1b4ade427ea07617f Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 8 Oct 2026 15:08:45 +0200 Subject: [PATCH 02/14] feat(project): Add logic for configPath parameter + Add tests --- packages/project/lib/graph/Workspace.js | 10 ++- .../library.d/custom-config-path.yaml | 10 +++ packages/project/test/lib/graph/Workspace.js | 74 ++++++++++++++++--- 3 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 packages/project/test/fixtures/library.d/custom-config-path.yaml diff --git a/packages/project/lib/graph/Workspace.js b/packages/project/lib/graph/Workspace.js index d39629a1491..57c4c233f92 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -144,7 +144,7 @@ class Workspace { `Missing property 'path' in dependency resolution configuration of workspace ${this.getName()}`); } return await this._getModulesFromPath( - this.#cwd, resolutionConfig.path); + this.#cwd, resolutionConfig.path, resolutionConfig.configPath); })); // Flatten array since package-workspaces might have resolved to multiple modules for a single resolution @@ -175,7 +175,7 @@ class Workspace { }; } - async _getModulesFromPath(cwd, relPath, failOnMissingFiles = true) { + async _getModulesFromPath(cwd, relPath, configPath, failOnMissingFiles = true) { const nodePath = path.join(cwd, relPath); if (this.#visitedNodePaths.has(nodePath)) { log.verbose(`Module located at ${nodePath} has already been visited`); @@ -235,7 +235,8 @@ class Workspace { const resolvedModules = new Map(); await Promise.all(searchPaths.map(async (pkgPath) => { - const modules = await this._getModulesFromPath(nodePath, pkgPath, staticPatterns.includes(pkgPath)); + const modules = + await this._getModulesFromPath(nodePath, pkgPath, configPath, staticPatterns.includes(pkgPath)); modules.forEach((module) => { const id = module.getId(); if (!resolvedModules.get(id)) { @@ -248,7 +249,8 @@ class Workspace { return [new Module({ id: pkg.name, version: pkg.version, - modulePath: nodePath + modulePath: nodePath, + configPath: configPath, })]; } } diff --git a/packages/project/test/fixtures/library.d/custom-config-path.yaml b/packages/project/test/fixtures/library.d/custom-config-path.yaml new file mode 100644 index 00000000000..ca7c0921253 --- /dev/null +++ b/packages/project/test/fixtures/library.d/custom-config-path.yaml @@ -0,0 +1,10 @@ +specVersion: "2.3" +type: library +metadata: + name: library.d + copyright: A custom config (called by configPath paramater) +resources: + configuration: + paths: + src: main/src + test: main/test diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index d7cc2e9399b..c78ac320a78 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -14,9 +14,9 @@ const collectionBLibraryA = path.join(__dirname, "..", "..", "fixtures", "collec const collectionBLibraryB = path.join(__dirname, "..", "..", "fixtures", "collection.b", "library.b"); const collectionBLibraryC = path.join(__dirname, "..", "..", "fixtures", "collection.b", "library.c"); -function createWorkspaceConfig({dependencyManagement}) { +function createWorkspaceConfig({dependencyManagement, specVersion = "workspace/1.0"}) { return { - specVersion: "workspace/1.0", + specVersion, metadata: { name: "workspace-name" }, @@ -474,15 +474,67 @@ test("Missing parameters", (t) => { }, "Threw with expected error message"); }); -test("Basic validation of workspace with specified configPath for dependency", (t) => { - // TODO: add test which passes the following yaml example: - // specVersion: workspace/1.1 - // metadata: - // name: default - // dependencyManagement: - // resolutions: - // - path: ../my-reuse-library - // configPath: ui5-mock.yaml +test("workspace/1.1: Basic validation with specified configPath for dependency", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/library.d", + configPath: "custom-config-path.yaml" // Custom configPath + }, { + path: "../../fixtures/library.e", + // no custom yaml -> resorts to default: ui5.yaml + }] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.deepEqual(Array.from(projectNameMap.keys()).sort(), ["library.d", "library.e"], "Correct project name keys"); + t.is(projectNameMap.get("library.d")._configPath, "custom-config-path.yaml"); + t.is(projectNameMap.get("library.e")._configPath, "ui5.yaml"); +}); + +test("workspace/1.1: Custom configPath for dependency but specVersion is workspace/1.0", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.0", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/library.d", + configPath: "custom-config-path.yaml" // Custom configPath + }, { + path: "../../fixtures/library.e", + }] + } + }) + }); + + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message.includes("\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher"), true); +}); +test("workspace/1.1: Custom configPath for dependency does not exist", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/library.d", + configPath: "not-existing-config-path.yaml" // Custom configPath + }, { + path: "../../fixtures/library.e", + // no custom yaml -> resorts to default: ui5.yaml + }] + } + }) + }); + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message.includes("Failed to read configuration for module library.d: " + + "Could not find configuration file in module at path 'not-existing-config-path.yaml'"), true); }); From 628498b0f9e9d6b685a9e7e7125c9bb6ceb0b815 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 8 Oct 2026 16:08:25 +0200 Subject: [PATCH 03/14] docs: Adjust Workspace.md page with configPath property --- .../documentation/docs/pages/Workspace.md | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/internal/documentation/docs/pages/Workspace.md b/internal/documentation/docs/pages/Workspace.md index 730e9420284..a588a33f721 100644 --- a/internal/documentation/docs/pages/Workspace.md +++ b/internal/documentation/docs/pages/Workspace.md @@ -5,7 +5,7 @@ ::: details ```yaml title="ui5-workspace.yaml" -specVersion: workspace/1.0 +specVersion: workspace/1.1 metadata: name: default dependencyManagement: @@ -23,14 +23,14 @@ Workspaces are typically configured in a `ui5-workspace.yaml` file, located next ::: details Example ```yaml title="ui5-workspace.yaml" -specVersion: workspace/1.0 +specVersion: workspace/1.1 metadata: name: default dependencyManagement: resolutions: - path: ../heavy.library --- -specVersion: workspace/1.0 +specVersion: workspace/1.1 metadata: name: extended dependencyManagement: @@ -52,14 +52,14 @@ This concept has been discussed in an RFC: [RFC 0006 Local Dependency Resolution A UI5 Workspace configuration must define a specification version (`specVersion`) compatible with its configuration. For more information, see [Workspace Specification Versions](#workspace-specification-versions). ```yaml -specVersion: "workspace/1.0" +specVersion: "workspace/1.1" ``` ### Metadata ::: details Example ```yaml -specVersion: workspace/1.0 +specVersion: workspace/1.1 metadata: name: dolphin ``` @@ -91,7 +91,7 @@ UI5 Workspace configurations allow to influence the dependency resolution when w ::: details ```yaml -specVersion: workspace/1.0 +specVersion: workspace/1.1 metadata: name: dolphin dependencyManagement: @@ -119,11 +119,24 @@ Paths must be written in POSIX (i.e. using only forward slashes `/` as path segm Note that this configuration only affects the resolution of dependencies which have already been found during the regular dependency resolution process of a project. For example, if a workspace resolution path resolves to a project that would otherwise not be part of the dependency tree of the current root project, it will not be added to the dependency tree. Also, transitive dependencies of resolved projects are not being followed. +#### configPath + +*Available since `workspace/1.1`* + +```yaml +dependencyManagement: + resolutions: + - path: ../test.library + configPath: custom-config.yaml +``` + +By default, UI5 CLI looks for a `ui5.yaml` at the root of the resolved module. The optional `configPath` property allows pointing to a different configuration file within the module directory. The path must be written in POSIX notation and must be **relative to the resolution `path`**. + ## Workspace Specification Versions A workspace configuration must define a Specification Version by setting the `specVersion` property. UI5 CLI uses this information to detect whether the currently installed version is compatible with a workspace's configuration. ```yaml -specVersion: "workspace/1.0" +specVersion: "workspace/1.1" [...] ``` @@ -142,8 +155,15 @@ Unless otherwise noted in the table below, UI5 CLI modules are backward-compatib Version | UI5 CLI Release --- | --- +**`workspace/1.1`** | v5.0.0+ and v4.1.0+ **`workspace/1.0`** | v3.0.0+ +### Specification Version `workspace/1.1` + +Added [`configPath`](#configpath) property to resolution entries. + +Specification Version `workspace/1.1` configurations are compatible with [UI5 CLI](https://github.com/SAP/ui5-cli) v5.0.0 and above, as well as v4.1.0 and above. + ### Specification Version `workspace/1.0` Initial version. From d7c329f87864b0589e6ab60cf1f446df6b848e5e Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 9 Oct 2026 12:02:58 +0200 Subject: [PATCH 04/14] refactor: Improve error handling when `path` is absolute + Forbid absolute paths for `configPath` property + Allow relative paths for `configPath` which are out-of-root (resolution dir) --- packages/project/lib/graph/Module.js | 2 +- packages/project/lib/graph/Workspace.js | 10 +++ .../library.e/ui5-library.d-config.yaml | 11 +++ packages/project/test/lib/graph/Workspace.js | 77 +++++++++++++++++-- 4 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 packages/project/test/fixtures/library.e/ui5-library.d-config.yaml diff --git a/packages/project/lib/graph/Module.js b/packages/project/lib/graph/Module.js index 5130ef2acf0..c7b9b5c5e08 100644 --- a/packages/project/lib/graph/Module.js +++ b/packages/project/lib/graph/Module.js @@ -69,7 +69,7 @@ class Module { this._id = id; this._version = version; this._modulePath = modulePath; - this._configPath = configPath || DEFAULT_CONFIG_PATH; + this._configPath = configPath ? path.resolve(modulePath, configPath) : DEFAULT_CONFIG_PATH; this._dependencies = Object.create(null); if (!Array.isArray(configuration)) { diff --git a/packages/project/lib/graph/Workspace.js b/packages/project/lib/graph/Workspace.js index 57c4c233f92..8add41dcc3e 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -143,6 +143,16 @@ class Workspace { throw new Error( `Missing property 'path' in dependency resolution configuration of workspace ${this.getName()}`); } + if (path.isAbsolute(resolutionConfig.path)) { + throw new Error( + `Absolute resolution path (${resolutionConfig.path}) is not supported in dependency resolution ` + + `configuration of workspace ${this.getName()}`); + } + if (resolutionConfig.configPath && path.isAbsolute(resolutionConfig.configPath)) { + throw new Error( + `Absolute configPath (${resolutionConfig.configPath}) is not supported in dependency resolution ` + + `configuration of workspace ${this.getName()}`); + } return await this._getModulesFromPath( this.#cwd, resolutionConfig.path, resolutionConfig.configPath); })); diff --git a/packages/project/test/fixtures/library.e/ui5-library.d-config.yaml b/packages/project/test/fixtures/library.e/ui5-library.d-config.yaml new file mode 100644 index 00000000000..a9f1cdca018 --- /dev/null +++ b/packages/project/test/fixtures/library.e/ui5-library.d-config.yaml @@ -0,0 +1,11 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.d + copyright: This is a config for another library (outside of this dir) +resources: + configuration: + paths: + src: main/src + test: main/test diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index c78ac320a78..0d3dac931b6 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -474,6 +474,23 @@ test("Missing parameters", (t) => { }, "Threw with expected error message"); }); +test("Absolute resolution path", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + dependencyManagement: { + resolutions: [{ + path: path.resolve(__dirname, "../../fixtures/library.d") + }] + } + }) + }); + + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message.includes(`Absolute resolution path (${path.resolve(__dirname, "../../fixtures/library.d")}) `+ + `is not supported in dependency resolution configuration of workspace ${workspace.getName()}`), true); +}); + test("workspace/1.1: Basic validation with specified configPath for dependency", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -493,7 +510,10 @@ test("workspace/1.1: Basic validation with specified configPath for dependency", const {projectNameMap} = await workspace._getResolvedModules(); t.deepEqual(Array.from(projectNameMap.keys()).sort(), ["library.d", "library.e"], "Correct project name keys"); - t.is(projectNameMap.get("library.d")._configPath, "custom-config-path.yaml"); + // Custom config: + t.is(projectNameMap.get("library.d")._configPath, + path.resolve(__dirname, "../../fixtures/library.d/custom-config-path.yaml")); + // Default config: t.is(projectNameMap.get("library.e")._configPath, "ui5.yaml"); }); @@ -517,6 +537,25 @@ test("workspace/1.1: Custom configPath for dependency but specVersion is workspa t.is(err.message.includes("\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher"), true); }); +test("workspace/1.1: Absolute custom configPath", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/library.d", + configPath: path.resolve(__dirname, "ui5.yaml") // Absolute configPath + }] + } + }) + }); + + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message.includes(`Absolute configPath (${path.resolve(__dirname, "ui5.yaml")}) ` + + `is not supported in dependency resolution configuration of workspace ${workspace.getName()}`), true); +}); + test("workspace/1.1: Custom configPath for dependency does not exist", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -525,16 +564,40 @@ test("workspace/1.1: Custom configPath for dependency does not exist", async (t) dependencyManagement: { resolutions: [{ path: "../../fixtures/library.d", - configPath: "not-existing-config-path.yaml" // Custom configPath - }, { - path: "../../fixtures/library.e", - // no custom yaml -> resorts to default: ui5.yaml + configPath: "not-existing-config-path.yaml" // Custom not-existing configPath }] } }) }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.is(err.message.includes("Failed to read configuration for module library.d: " + - "Could not find configuration file in module at path 'not-existing-config-path.yaml'"), true); + t.is(err.message.includes(`Failed to read configuration for module library.d at `+ + `'${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'. ` + + `Error: ENOENT: no such file or directory, `+ + `open '${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'`), true); +}); + +test("workspace/1.1: Custom relative configPath which is outside of the module dir", async (t) => { + // This test covers a scenario where the custom relative configPath + // points to a location outside of the module directory. + // Config for library.d is located in library.e: + + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/library.d", + configPath: "../library.e/ui5-library.d-config.yaml" // Out-of-root path + }] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.deepEqual(Array.from(projectNameMap.keys()).sort(), ["library.d"], "Correct project name key"); + // Returns absolute path of other library config: + t.is(projectNameMap.get("library.d")._configPath, + path.resolve(__dirname, "../../fixtures/library.e/ui5-library.d-config.yaml")); }); From 553b6db5a52d9fff9e5082d69b46619007f6c727 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 9 Oct 2026 18:20:51 +0200 Subject: [PATCH 05/14] refactor: Adjust workspace config to fix tests --- .../lib/validation/schema/ui5-workspace.json | 8 ++- .../lib/validation/schema/ui5-workspace.js | 57 ++++++++++++++++++- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/packages/project/lib/validation/schema/ui5-workspace.json b/packages/project/lib/validation/schema/ui5-workspace.json index f7a4bf76826..7b2916daa3b 100644 --- a/packages/project/lib/validation/schema/ui5-workspace.json +++ b/packages/project/lib/validation/schema/ui5-workspace.json @@ -28,8 +28,12 @@ "properties": { "resolutions": { "items": { - "not": {"required": ["configPath"]}, - "errorMessage": "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher" + "if": {"type": "object"}, + "then": { + "not": {"required": ["configPath"]}, + "errorMessage": "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher" + }, + "else": {} } } } diff --git a/packages/project/test/lib/validation/schema/ui5-workspace.js b/packages/project/test/lib/validation/schema/ui5-workspace.js index 232f5965991..8279a0d93b3 100644 --- a/packages/project/test/lib/validation/schema/ui5-workspace.js +++ b/packages/project/test/lib/validation/schema/ui5-workspace.js @@ -89,7 +89,7 @@ For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-sp message: "must be equal to one of the allowed values", params: { - allowedValues: ["workspace/1.0"], + allowedValues: ["workspace/1.1", "workspace/1.0"], }, }, ], @@ -290,7 +290,7 @@ For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-sp message: "must be equal to one of the allowed values", params: { - allowedValues: ["workspace/1.0"], + allowedValues: ["workspace/1.1", "workspace/1.0"], }, }, ], @@ -351,7 +351,7 @@ For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-sp message: "must be equal to one of the allowed values", params: { - allowedValues: ["workspace/1.0"], + allowedValues: ["workspace/1.1", "workspace/1.0"], }, }, ], @@ -456,3 +456,54 @@ test("Invalid dependencyManagement", async (t) => { ] ); }); + +test("Invalid dependencyManagement: configPath not allowed for workspace/1.0", async (t) => { + await assertValidation( + t, + { + specVersion: "workspace/1.0", + metadata: { + name: "test-spec-name", + }, + dependencyManagement: { + resolutions: [ + { + path: "../my.library", + configPath: "config/ui5.yaml" + }], + }, + }, + [ + { + instancePath: "/dependencyManagement/resolutions/0", + keyword: "errorMessage", + message: "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher", + params: { + errors: [ + { + instancePath: "/dependencyManagement/resolutions/0", + keyword: "not", + message: "must NOT be valid", + params: {}, + }, + ], + }, + }, + ] + ); +}); + +test("Valid dependencyManagement: configPath allowed for workspace/1.1", async (t) => { + await assertValidation( + t, + { + specVersion: "workspace/1.1", + metadata: { + name: "test-spec-name", + }, + dependencyManagement: { + resolutions: [{path: "../my.library", configPath: "config/ui5.yaml"}], + }, + } + ); +}); From 5c857ed3631c5add9dc3e299765e15fa262751e3 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Mon, 12 Oct 2026 15:27:59 +0200 Subject: [PATCH 06/14] test(project): Adjust resolutions path to absolute --- packages/project/lib/graph/Workspace.js | 8 +++--- packages/project/test/lib/graph/Module.js | 14 +++++++---- packages/project/test/lib/graph/Workspace.js | 26 ++++++++++++++------ 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/packages/project/lib/graph/Workspace.js b/packages/project/lib/graph/Workspace.js index 8add41dcc3e..83990fda630 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -145,13 +145,13 @@ class Workspace { } if (path.isAbsolute(resolutionConfig.path)) { throw new Error( - `Absolute resolution path (${resolutionConfig.path}) is not supported in dependency resolution ` + - `configuration of workspace ${this.getName()}`); + `Absolute paths are not allowed in workspace configurations of dependencies. ` + + `Found '${resolutionConfig.path}' in workspace '${this.getName()}'`); } if (resolutionConfig.configPath && path.isAbsolute(resolutionConfig.configPath)) { throw new Error( - `Absolute configPath (${resolutionConfig.configPath}) is not supported in dependency resolution ` + - `configuration of workspace ${this.getName()}`); + `Absolute config paths are not allowed in workspace configurations of dependencies. ` + + `Found '${resolutionConfig.configPath}' in workspace '${this.getName()}'`); } return await this._getModulesFromPath( this.#cwd, resolutionConfig.path, resolutionConfig.configPath); diff --git a/packages/project/test/lib/graph/Module.js b/packages/project/test/lib/graph/Module.js index abd57d90860..d94e6aafd4b 100644 --- a/packages/project/test/lib/graph/Module.js +++ b/packages/project/test/lib/graph/Module.js @@ -433,9 +433,11 @@ test("Corrupt configuration in file", async (t) => { }); const err = await t.throwsAsync(ui5Module.getSpecifications()); - t.regex(err.message, - new RegExp("^Failed to parse configuration for project application.a.id at 'ui5-test-corrupt.yaml'.*"), - "Threw with parsing error"); + t.log(err.message); + t.true(err.message.startsWith( + `Failed to parse configuration for project application.a.id at ` + + `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-test-corrupt.yaml")}'`), + "Threw with parsing error"); }); test("Merge keys (<<:) in ui5.yaml are resolved and not left as literal keys", async (t) => { @@ -511,8 +513,10 @@ test("Incorrect config path", async (t) => { const err = await t.throwsAsync(ui5Module.getSpecifications()); t.is(err.message, - "Failed to read configuration for module application.a.id: " + - "Could not find configuration file in module at path 'ui5-does-not-exist.yaml'", + `Failed to read configuration for module application.a.id at `+ + `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-does-not-exist.yaml")}'. `+ + `Error: ENOENT: no such file or directory, open `+ + `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-does-not-exist.yaml")}'`, "Threw with expected error message"); }); diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 0d3dac931b6..0bd426f3567 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -487,8 +487,10 @@ test("Absolute resolution path", async (t) => { }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.is(err.message.includes(`Absolute resolution path (${path.resolve(__dirname, "../../fixtures/library.d")}) `+ - `is not supported in dependency resolution configuration of workspace ${workspace.getName()}`), true); + t.log(err.message); + t.true(err.message.startsWith(`Absolute paths are not allowed in workspace configurations of dependencies. `+ + `Found '${path.resolve(__dirname, "../../fixtures/library.d")}' in workspace '${workspace.getName()}'`), + "Starts with expected error message"); }); test("workspace/1.1: Basic validation with specified configPath for dependency", async (t) => { @@ -534,7 +536,9 @@ test("workspace/1.1: Custom configPath for dependency but specVersion is workspa }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.is(err.message.includes("\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher"), true); + t.log(err.message); + t.true(err.message.includes("\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher"), + "Starts with expected error message"); }); test("workspace/1.1: Absolute custom configPath", async (t) => { @@ -545,15 +549,19 @@ test("workspace/1.1: Absolute custom configPath", async (t) => { dependencyManagement: { resolutions: [{ path: "../../fixtures/library.d", - configPath: path.resolve(__dirname, "ui5.yaml") // Absolute configPath + configPath: path.resolve(__dirname, "../../fixtures/library.d/ui5.yaml") // Absolute configPath }] } }) }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.is(err.message.includes(`Absolute configPath (${path.resolve(__dirname, "ui5.yaml")}) ` + - `is not supported in dependency resolution configuration of workspace ${workspace.getName()}`), true); + t.log(err.message); + t.true(err.message.startsWith(`Absolute config paths ` + + `are not allowed in workspace configurations of dependencies. ` + + `Found '${path.resolve(__dirname, "../../fixtures/library.d/ui5.yaml")}' `+ + `in workspace 'workspace-name'`), + "Starts with expected error message"); }); test("workspace/1.1: Custom configPath for dependency does not exist", async (t) => { @@ -571,10 +579,12 @@ test("workspace/1.1: Custom configPath for dependency does not exist", async (t) }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.is(err.message.includes(`Failed to read configuration for module library.d at `+ + t.log(err.message); + t.true(err.message.startsWith(`Failed to read configuration for module library.d at `+ `'${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'. ` + `Error: ENOENT: no such file or directory, `+ - `open '${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'`), true); + `open '${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'`), + "Starts with expected error message"); }); test("workspace/1.1: Custom relative configPath which is outside of the module dir", async (t) => { From 0dcf01a4df5c58011ee3df1ec7ba712036952d26 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 13 Oct 2026 10:00:30 +0200 Subject: [PATCH 07/14] refactor: Remove "else" block in schema + Adjust tests --- packages/project/lib/validation/schema/ui5-workspace.json | 3 +-- packages/project/test/lib/graph/Module.js | 7 +++---- packages/project/test/lib/graph/Workspace.js | 4 ---- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/project/lib/validation/schema/ui5-workspace.json b/packages/project/lib/validation/schema/ui5-workspace.json index 7b2916daa3b..7f75094dbf5 100644 --- a/packages/project/lib/validation/schema/ui5-workspace.json +++ b/packages/project/lib/validation/schema/ui5-workspace.json @@ -32,8 +32,7 @@ "then": { "not": {"required": ["configPath"]}, "errorMessage": "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher" - }, - "else": {} + } } } } diff --git a/packages/project/test/lib/graph/Module.js b/packages/project/test/lib/graph/Module.js index d94e6aafd4b..a70685342bc 100644 --- a/packages/project/test/lib/graph/Module.js +++ b/packages/project/test/lib/graph/Module.js @@ -433,11 +433,10 @@ test("Corrupt configuration in file", async (t) => { }); const err = await t.throwsAsync(ui5Module.getSpecifications()); - t.log(err.message); t.true(err.message.startsWith( `Failed to parse configuration for project application.a.id at ` + `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-test-corrupt.yaml")}'`), - "Threw with parsing error"); + `Should throw with parsing error.`); }); test("Merge keys (<<:) in ui5.yaml are resolved and not left as literal keys", async (t) => { @@ -517,7 +516,7 @@ test("Incorrect config path", async (t) => { `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-does-not-exist.yaml")}'. `+ `Error: ENOENT: no such file or directory, open `+ `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-does-not-exist.yaml")}'`, - "Threw with expected error message"); + "Should throw with parsing error."); }); test("Incorrect absolute config path", async (t) => { @@ -532,7 +531,7 @@ test("Incorrect absolute config path", async (t) => { t.true(err.message.startsWith( `Failed to read configuration for module application.a.id at '${configPath}'. Error:`), - "Threw with expected error message"); + "Should throw with parsing error."); }); test("Module without ui5.yaml is ignored", async (t) => { diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 0bd426f3567..18532786bbc 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -487,7 +487,6 @@ test("Absolute resolution path", async (t) => { }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.log(err.message); t.true(err.message.startsWith(`Absolute paths are not allowed in workspace configurations of dependencies. `+ `Found '${path.resolve(__dirname, "../../fixtures/library.d")}' in workspace '${workspace.getName()}'`), "Starts with expected error message"); @@ -536,7 +535,6 @@ test("workspace/1.1: Custom configPath for dependency but specVersion is workspa }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.log(err.message); t.true(err.message.includes("\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher"), "Starts with expected error message"); }); @@ -556,7 +554,6 @@ test("workspace/1.1: Absolute custom configPath", async (t) => { }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.log(err.message); t.true(err.message.startsWith(`Absolute config paths ` + `are not allowed in workspace configurations of dependencies. ` + `Found '${path.resolve(__dirname, "../../fixtures/library.d/ui5.yaml")}' `+ @@ -579,7 +576,6 @@ test("workspace/1.1: Custom configPath for dependency does not exist", async (t) }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.log(err.message); t.true(err.message.startsWith(`Failed to read configuration for module library.d at `+ `'${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'. ` + `Error: ENOENT: no such file or directory, `+ From 4ebdb7311ecd7ddc5924956d0bc11dfc0d4b7cb6 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 13 Oct 2026 15:25:49 +0200 Subject: [PATCH 08/14] refactor: Fix AJV strict mode warnings --- packages/project/lib/validation/schema/ui5-workspace.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/project/lib/validation/schema/ui5-workspace.json b/packages/project/lib/validation/schema/ui5-workspace.json index 7f75094dbf5..bfc61a3ba29 100644 --- a/packages/project/lib/validation/schema/ui5-workspace.json +++ b/packages/project/lib/validation/schema/ui5-workspace.json @@ -25,12 +25,17 @@ "then": { "properties": { "dependencyManagement": { + "type": "object", "properties": { "resolutions": { + "type": "array", "items": { "if": {"type": "object"}, "then": { - "not": {"required": ["configPath"]}, + "not": { + "type": "object", + "required": ["configPath"] + }, "errorMessage": "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher" } } From a1e309bf3f2aff650fb936de58b8c315d47babdc Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 13 Oct 2026 16:59:08 +0200 Subject: [PATCH 09/14] docs: Remove "v5.0.0+" and only keep "v4.1.0+" --- internal/documentation/docs/pages/Workspace.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/documentation/docs/pages/Workspace.md b/internal/documentation/docs/pages/Workspace.md index a588a33f721..441ea33e611 100644 --- a/internal/documentation/docs/pages/Workspace.md +++ b/internal/documentation/docs/pages/Workspace.md @@ -155,14 +155,14 @@ Unless otherwise noted in the table below, UI5 CLI modules are backward-compatib Version | UI5 CLI Release --- | --- -**`workspace/1.1`** | v5.0.0+ and v4.1.0+ +**`workspace/1.1`** | v4.1.0+ **`workspace/1.0`** | v3.0.0+ ### Specification Version `workspace/1.1` Added [`configPath`](#configpath) property to resolution entries. -Specification Version `workspace/1.1` configurations are compatible with [UI5 CLI](https://github.com/SAP/ui5-cli) v5.0.0 and above, as well as v4.1.0 and above. +Specification Version `workspace/1.1` configurations are compatible with [UI5 CLI](https://github.com/UI5/cli) v4.1.0 and above. ### Specification Version `workspace/1.0` From 92b7388ef6b5c3cf7afa928d38aa46e1fbad0ebc Mon Sep 17 00:00:00 2001 From: Max Reichmann <91143102+maxreichmann@users.noreply.github.com> Date: Wed, 14 Oct 2026 13:16:37 +0200 Subject: [PATCH 10/14] docs: Apply @d3xter666 suggestion Co-authored-by: Yavor Ivanov --- packages/project/lib/validation/schema/ui5-workspace.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/project/lib/validation/schema/ui5-workspace.json b/packages/project/lib/validation/schema/ui5-workspace.json index bfc61a3ba29..ab2b5d5abea 100644 --- a/packages/project/lib/validation/schema/ui5-workspace.json +++ b/packages/project/lib/validation/schema/ui5-workspace.json @@ -10,7 +10,7 @@ "properties": { "specVersion": { "enum": ["workspace/1.1", "workspace/1.0"], - "errorMessage": "Unsupported \"specVersion\"\nYour UI5 CLI installation might be outdated.\nSupported specification versions: \"workspace/1.0\"\nFor details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions" + "errorMessage": "Unsupported \"specVersion\"\nYour UI5 CLI installation might be outdated.\nSupported specification versions: \"workspace/1.0\", \"workspace/1.1\".\nFor details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions" }, "metadata": { "$ref": "#/definitions/metadata" From 64f03a2ecddd62b1a841b05dcae0472f5bba4392 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 15 Oct 2026 16:43:18 +0200 Subject: [PATCH 11/14] refactor: Add temp test matrix (DELETE LATER) --- configPath-test-matrix.md | 420 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 420 insertions(+) create mode 100644 configPath-test-matrix.md diff --git a/configPath-test-matrix.md b/configPath-test-matrix.md new file mode 100644 index 00000000000..e06f2d4f2d4 --- /dev/null +++ b/configPath-test-matrix.md @@ -0,0 +1,420 @@ +TODO: Delete this file before merging + +# `configPath` Test Matrix + +## Scenario 1 + +### GIVEN for all cases: + +``` +root.collection/ +├── library.a/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +├── library.b/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +└── collection.a/ + ├── library.c/ + │ ├── ui5.yaml + │ ├── ui5-custom.yaml + │ └── ui5-another-custom.yaml + └── library.d/ + ├── ui5.yaml + ├── ui5-custom.yaml + └── ui5-another-custom.yaml +``` + +--- + +### Case 1 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + + +### Case 2 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + + +### Case 3 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 4 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection # overwrites the prev. config entirely +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 5 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml + - path: ../root.collection/library.a +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 6 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.b + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5-another-custom.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 7 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-another-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). + +### Case 8 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 9 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml + - path: ../root.collection/collection.a/library.c + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-another-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +--- +--- + +## Scenario 2 + +### GIVEN for all cases: + +``` +root.collection/ +├── library.a/ +│ ├── ui5.yaml +│ │ # missing ui5-custom.yaml +│ └── ui5-another-custom.yaml +├── library.b/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +└── collection.a/ + ├── library.c/ + │ ├── ui5.yaml + │ ├── ui5-custom.yaml + │ └── ui5-another-custom.yaml + └── library.d/ + ├── ui5.yaml + ├── ui5-custom.yaml + └── ui5-another-custom.yaml +``` + +--- + +### Case 1 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + + +### Case 2 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml +``` + +**THEN** + +Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` + + +### Case 3 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml +``` + +**THEN** + +Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` + +### Case 4 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection # overwrites the prev. config entirely +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 5 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml + - path: ../root.collection/library.a +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 6 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-another-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). From c7f740b038ba5beb4652d056cde7b3a671d6cd4c Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 16 Oct 2026 13:32:09 +0200 Subject: [PATCH 12/14] test(project): Fix schema tests --- .../project/test/lib/validation/schema/ui5-workspace.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/project/test/lib/validation/schema/ui5-workspace.js b/packages/project/test/lib/validation/schema/ui5-workspace.js index 8279a0d93b3..60cd0a80343 100644 --- a/packages/project/test/lib/validation/schema/ui5-workspace.js +++ b/packages/project/test/lib/validation/schema/ui5-workspace.js @@ -79,7 +79,7 @@ test("Empty config", async (t) => { keyword: "errorMessage", message: `Unsupported "specVersion" Your UI5 CLI installation might be outdated. -Supported specification versions: "workspace/1.0" +Supported specification versions: "workspace/1.0", "workspace/1.1". For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions`, params: { errors: [ @@ -280,7 +280,7 @@ test("Invalid fields", async (t) => { keyword: "errorMessage", message: `Unsupported "specVersion" Your UI5 CLI installation might be outdated. -Supported specification versions: "workspace/1.0" +Supported specification versions: "workspace/1.0", "workspace/1.1". For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions`, params: { errors: [ @@ -341,7 +341,7 @@ test("Invalid types", async (t) => { keyword: "errorMessage", message: `Unsupported "specVersion" Your UI5 CLI installation might be outdated. -Supported specification versions: "workspace/1.0" +Supported specification versions: "workspace/1.0", "workspace/1.1". For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions`, params: { errors: [ From 09eb87e8b07151d3006299738ab3fd59829737d9 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 16 Oct 2026 16:01:14 +0200 Subject: [PATCH 13/14] test: Add scenario for configPath in collections --- packages/project/lib/graph/Module.js | 7 ++++--- packages/project/lib/graph/Workspace.js | 2 +- .../collection.a/library.c/package.json | 5 +++++ .../library.c/src/library/a/.library | 17 +++++++++++++++ .../library/a/themes/base/library.source.less | 6 ++++++ .../library.c/ui5-another-custom.yaml | 5 +++++ .../collection.a/library.c/ui5-custom.yaml | 5 +++++ .../collection.a/library.c/ui5.yaml | 5 +++++ .../collection.a/library.d/package.json | 5 +++++ .../library.d/src/library/b/.library | 17 +++++++++++++++ .../library.d/ui5-another-custom.yaml | 5 +++++ .../collection.a/library.d/ui5-custom.yaml | 5 +++++ .../collection.a/library.d/ui5.yaml | 5 +++++ .../collection.c/collection.a/package.json | 9 ++++++++ .../collection.c/library.a/package.json | 5 +++++ .../library.a/src/library/a/.library | 17 +++++++++++++++ .../library/a/themes/base/library.source.less | 6 ++++++ .../library.a/ui5-another-custom.yaml | 5 +++++ .../collection.c/library.a/ui5-custom.yaml | 5 +++++ .../fixtures/collection.c/library.a/ui5.yaml | 5 +++++ .../collection.c/library.b/package.json | 5 +++++ .../library.b/src/library/b/.library | 17 +++++++++++++++ .../library.b/ui5-another-custom.yaml | 5 +++++ .../collection.c/library.b/ui5-custom.yaml | 5 +++++ .../fixtures/collection.c/library.b/ui5.yaml | 5 +++++ .../test/fixtures/collection.c/package.json | 10 +++++++++ packages/project/test/lib/graph/Workspace.js | 21 +++++++++++++++++++ 27 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/package.json create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/.library create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.d/package.json create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.d/src/library/b/.library create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.d/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/package.json create mode 100644 packages/project/test/fixtures/collection.c/library.a/package.json create mode 100644 packages/project/test/fixtures/collection.c/library.a/src/library/a/.library create mode 100644 packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less create mode 100644 packages/project/test/fixtures/collection.c/library.a/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/library.a/ui5-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/library.a/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c/library.b/package.json create mode 100644 packages/project/test/fixtures/collection.c/library.b/src/library/b/.library create mode 100644 packages/project/test/fixtures/collection.c/library.b/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/library.b/ui5-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/library.b/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c/package.json diff --git a/packages/project/lib/graph/Module.js b/packages/project/lib/graph/Module.js index c7b9b5c5e08..5bf5223a827 100644 --- a/packages/project/lib/graph/Module.js +++ b/packages/project/lib/graph/Module.js @@ -69,7 +69,7 @@ class Module { this._id = id; this._version = version; this._modulePath = modulePath; - this._configPath = configPath ? path.resolve(modulePath, configPath) : DEFAULT_CONFIG_PATH; + this._configPath = configPath || DEFAULT_CONFIG_PATH; this._dependencies = Object.create(null); if (!Array.isArray(configuration)) { @@ -308,8 +308,9 @@ class Module { } if (!configResource) { if (configPath !== DEFAULT_CONFIG_PATH) { - throw new Error("Failed to read configuration for module " + - `${this.getId()}: Could not find configuration file in module at path '${configPath}'`); + throw new Error(`Failed to read configuration for module ` + + `${this.getId()}: Could not find configuration file in module at path `+ + `'${this._configPath}'`); } return null; } diff --git a/packages/project/lib/graph/Workspace.js b/packages/project/lib/graph/Workspace.js index 83990fda630..e47f9556cc1 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -260,7 +260,7 @@ class Workspace { id: pkg.name, version: pkg.version, modulePath: nodePath, - configPath: configPath, + configPath: configPath ? path.resolve(nodePath, configPath) : undefined, })]; } } diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/package.json b/packages/project/test/fixtures/collection.c/collection.a/library.c/package.json new file mode 100644 index 00000000000..efbb5e86409 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.c", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/.library b/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/.library new file mode 100644 index 00000000000..ef0ea1065bc --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/.library @@ -0,0 +1,17 @@ + + + + library.a + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library A + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less b/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less new file mode 100644 index 00000000000..ff0f1d5e3df --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less @@ -0,0 +1,6 @@ +@libraryAColor1: lightgoldenrodyellow; + +.library-a-foo { + color: @libraryAColor1; + padding: 1px 2px 3px 4px; +} diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-another-custom.yaml new file mode 100644 index 00000000000..90edaf85c9f --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.c.another.custom diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-custom.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-custom.yaml new file mode 100644 index 00000000000..c84f8a4e014 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.c.custom diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5.yaml new file mode 100644 index 00000000000..7c5e38a7fc1 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.c diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.d/package.json b/packages/project/test/fixtures/collection.c/collection.a/library.d/package.json new file mode 100644 index 00000000000..fd53171e802 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.d/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.d", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.d/src/library/b/.library b/packages/project/test/fixtures/collection.c/collection.a/library.d/src/library/b/.library new file mode 100644 index 00000000000..7128151f3f4 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.d/src/library/b/.library @@ -0,0 +1,17 @@ + + + + library.b + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library B + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-another-custom.yaml new file mode 100644 index 00000000000..82c1f167712 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.d.another.custom diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-custom.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-custom.yaml new file mode 100644 index 00000000000..47d355df4f9 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.d.custom diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5.yaml new file mode 100644 index 00000000000..e05b61880d8 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.d diff --git a/packages/project/test/fixtures/collection.c/collection.a/package.json b/packages/project/test/fixtures/collection.c/collection.a/package.json new file mode 100644 index 00000000000..b4e3eb86aea --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/package.json @@ -0,0 +1,9 @@ +{ + "name": "collection", + "version": "1.0.0", + "description": "Simple Collection", + "workspaces": [ + "library.c", + "library.d" + ] +} diff --git a/packages/project/test/fixtures/collection.c/library.a/package.json b/packages/project/test/fixtures/collection.c/library.a/package.json new file mode 100644 index 00000000000..3529634765e --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.a", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c/library.a/src/library/a/.library b/packages/project/test/fixtures/collection.c/library.a/src/library/a/.library new file mode 100644 index 00000000000..ef0ea1065bc --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/src/library/a/.library @@ -0,0 +1,17 @@ + + + + library.a + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library A + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less b/packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less new file mode 100644 index 00000000000..ff0f1d5e3df --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less @@ -0,0 +1,6 @@ +@libraryAColor1: lightgoldenrodyellow; + +.library-a-foo { + color: @libraryAColor1; + padding: 1px 2px 3px 4px; +} diff --git a/packages/project/test/fixtures/collection.c/library.a/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c/library.a/ui5-another-custom.yaml new file mode 100644 index 00000000000..a1fa609975f --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.a.another.custom diff --git a/packages/project/test/fixtures/collection.c/library.a/ui5-custom.yaml b/packages/project/test/fixtures/collection.c/library.a/ui5-custom.yaml new file mode 100644 index 00000000000..62e46e14554 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/ui5-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.a.custom diff --git a/packages/project/test/fixtures/collection.c/library.a/ui5.yaml b/packages/project/test/fixtures/collection.c/library.a/ui5.yaml new file mode 100644 index 00000000000..8d4784313c3 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.a diff --git a/packages/project/test/fixtures/collection.c/library.b/package.json b/packages/project/test/fixtures/collection.c/library.b/package.json new file mode 100644 index 00000000000..a1f0a5e1650 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.b/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.b", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c/library.b/src/library/b/.library b/packages/project/test/fixtures/collection.c/library.b/src/library/b/.library new file mode 100644 index 00000000000..7128151f3f4 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.b/src/library/b/.library @@ -0,0 +1,17 @@ + + + + library.b + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library B + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c/library.b/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c/library.b/ui5-another-custom.yaml new file mode 100644 index 00000000000..4c7d3de243b --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.b/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.b.another.custom diff --git a/packages/project/test/fixtures/collection.c/library.b/ui5-custom.yaml b/packages/project/test/fixtures/collection.c/library.b/ui5-custom.yaml new file mode 100644 index 00000000000..52a13d705f2 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.b/ui5-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.b.custom diff --git a/packages/project/test/fixtures/collection.c/library.b/ui5.yaml b/packages/project/test/fixtures/collection.c/library.b/ui5.yaml new file mode 100644 index 00000000000..b2fe5be59ee --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.b/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.b diff --git a/packages/project/test/fixtures/collection.c/package.json b/packages/project/test/fixtures/collection.c/package.json new file mode 100644 index 00000000000..edb7e2145da --- /dev/null +++ b/packages/project/test/fixtures/collection.c/package.json @@ -0,0 +1,10 @@ +{ + "name": "collection", + "version": "1.0.0", + "description": "Simple Collection", + "workspaces": [ + "library.a", + "library.b", + "collection.a" + ] +} diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 18532786bbc..e12fb64fb36 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -607,3 +607,24 @@ test("workspace/1.1: Custom relative configPath which is outside of the module d t.is(projectNameMap.get("library.d")._configPath, path.resolve(__dirname, "../../fixtures/library.e/ui5-library.d-config.yaml")); }); + + +test("workspace/1.1: Nested library collection scenario", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c", + }] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a")._configPath, `ui5.yaml`); + t.is(projectNameMap.get("library.b")._configPath, `ui5.yaml`); + t.is(projectNameMap.get("library.c")._configPath, `ui5.yaml`); + t.is(projectNameMap.get("library.d")._configPath, `ui5.yaml`); +}); From bbda6716c9f162d946f067d96c60c903fd76c081 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 16 Oct 2026 17:01:38 +0200 Subject: [PATCH 14/14] test(project): Add first test case for collection scenarios --- packages/project/test/lib/graph/Workspace.js | 38 ++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index e12fb64fb36..f8d1f4426ff 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -492,7 +492,7 @@ test("Absolute resolution path", async (t) => { "Starts with expected error message"); }); -test("workspace/1.1: Basic validation with specified configPath for dependency", async (t) => { +test("[workspace/1.1] Basic validation with specified configPath for dependency", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -518,7 +518,7 @@ test("workspace/1.1: Basic validation with specified configPath for dependency", t.is(projectNameMap.get("library.e")._configPath, "ui5.yaml"); }); -test("workspace/1.1: Custom configPath for dependency but specVersion is workspace/1.0", async (t) => { +test("[workspace/1.1] Custom configPath for dependency but specVersion is workspace/1.0", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -539,7 +539,7 @@ test("workspace/1.1: Custom configPath for dependency but specVersion is workspa "Starts with expected error message"); }); -test("workspace/1.1: Absolute custom configPath", async (t) => { +test("[workspace/1.1] Absolute custom configPath", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -561,7 +561,7 @@ test("workspace/1.1: Absolute custom configPath", async (t) => { "Starts with expected error message"); }); -test("workspace/1.1: Custom configPath for dependency does not exist", async (t) => { +test("[workspace/1.1] Custom configPath for dependency does not exist", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -583,7 +583,7 @@ test("workspace/1.1: Custom configPath for dependency does not exist", async (t) "Starts with expected error message"); }); -test("workspace/1.1: Custom relative configPath which is outside of the module dir", async (t) => { +test("[workspace/1.1] Custom relative configPath which is outside of the module dir", async (t) => { // This test covers a scenario where the custom relative configPath // points to a location outside of the module directory. // Config for library.d is located in library.e: @@ -609,7 +609,7 @@ test("workspace/1.1: Custom relative configPath which is outside of the module d }); -test("workspace/1.1: Nested library collection scenario", async (t) => { +test("[workspace/1.1] Nested library collection scenario", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -628,3 +628,29 @@ test("workspace/1.1: Nested library collection scenario", async (t) => { t.is(projectNameMap.get("library.c")._configPath, `ui5.yaml`); t.is(projectNameMap.get("library.d")._configPath, `ui5.yaml`); }); + +test("[workspace/1.1] No configPath", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c", + configPath: "ui5-custom.yaml" + }] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a.custom")._configPath, + path.resolve(__dirname, `../../fixtures/collection.c/library.a/ui5-custom.yaml`)); + t.is(projectNameMap.get("library.b.custom")._configPath, + path.resolve(__dirname, `../../fixtures/collection.c/library.b/ui5-custom.yaml`)); + t.is(projectNameMap.get("library.c.custom")._configPath, + path.resolve(__dirname, `../../fixtures/collection.c/collection.a/library.c/ui5-custom.yaml`)); + t.is(projectNameMap.get("library.d.custom")._configPath, + path.resolve(__dirname, `../../fixtures/collection.c/collection.a/library.d/ui5-custom.yaml`)); +}); +