diff --git a/lib/graph/Workspace.js b/lib/graph/Workspace.js
index a3963182d..ad0c850f2 100644
--- a/lib/graph/Workspace.js
+++ b/lib/graph/Workspace.js
@@ -74,6 +74,20 @@ class Workspace {
return this.#configuration.metadata.name;
}
+ /**
+ * Returns an array of [Module]{@ui5/project/graph/Module} instances found in the configured
+ * dependency-management resolution paths of this workspace, sorted by module ID.
+ *
+ * @public
+ * @returns {Promise<@ui5/project/graph/Module[]>}
+ * Array of Module instances sorted by module ID
+ */
+ async getModules() {
+ const {moduleIdMap} = await this._getResolvedModules();
+ const sortedMap = new Map([...moduleIdMap].sort((a, b) => String(a[0]).localeCompare(b[0])));
+ return Array.from(sortedMap.values());
+ }
+
/**
* For a given project name (e.g. the value of the metadata.name
property in a ui5.yaml),
* returns a [Module]{@ui5/project/graph/Module} instance or undefined
depending on whether the project
@@ -82,7 +96,7 @@ class Workspace {
* @public
* @param {string} projectName Name of the project
* @returns {Promise<@ui5/project/graph/Module|undefined>}
- * Module instance of undefined
if none is found
+ * Module instance, or undefined
if none is found
*/
async getModuleByProjectName(projectName) {
const {projectNameMap} = await this._getResolvedModules();
@@ -98,7 +112,7 @@ class Workspace {
* @public
* @param {string} nodeId Node ID of the module
* @returns {Promise<@ui5/project/graph/Module|undefined>}
- * Module instance of undefined
if none is found
+ * Module instance, or undefined
if none is found
*/
async getModuleByNodeId(nodeId) {
const {moduleIdMap} = await this._getResolvedModules();
diff --git a/lib/specifications/Project.js b/lib/specifications/Project.js
index cdb236bae..2ce1fc8c4 100644
--- a/lib/specifications/Project.js
+++ b/lib/specifications/Project.js
@@ -61,7 +61,8 @@ class Project extends Specification {
* @returns {boolean} True if the project is a framework project
*/
isFrameworkProject() {
- return this.__id.startsWith("@openui5/") || this.__id.startsWith("@sapui5/");
+ const id = this.getId();
+ return id.startsWith("@openui5/") || id.startsWith("@sapui5/");
}
/**
diff --git a/lib/specifications/Specification.js b/lib/specifications/Specification.js
index 361cf4066..5c2af4142 100644
--- a/lib/specifications/Specification.js
+++ b/lib/specifications/Specification.js
@@ -97,8 +97,10 @@ class Specification {
this._version = version;
this._modulePath = modulePath;
- // The configured name (metadata.name) should be the unique identifier
- // The ID property as supplied by the translators is only here for debugging and potential tracing purposes
+ // The ID property is filled from the provider (e.g. package.json "name") and might differ between providers.
+ // It is mainly used to detect framework libraries marked by @openui5 / @sapui5 scopes of npm package.
+ // (see Project#isFrameworkProject)
+ // In general, the configured name (metadata.name) should be used instead as the unique identifier of a project.
this.__id = id;
// Deep clone config to prevent changes by reference
@@ -157,7 +159,23 @@ class Specification {
/* === Attributes === */
/**
- * Get the name of this specification
+ * Gets the ID of this specification.
+ *
+ *
Note: Only to be used for special occasions as it is specific to the provider that was used and does + * not necessarily represent something defined by the project.
+ * + * For general purposes of a unique identifier use + * {@link @ui5/project/specifications/Specification#getName getName} instead. + * + * @public + * @returns {string} Specification ID + */ + getId() { + return this.__id; + } + + /** + * Gets the name of this specification. Represents a unique identifier. * * @public * @returns {string} Specification name @@ -167,7 +185,7 @@ class Specification { } /** - * Get the kind of this specification, for exampleproject
or extension
+ * Gets the kind of this specification, for example project
or extension
*
* @public
* @returns {string} Specification kind
@@ -177,7 +195,7 @@ class Specification {
}
/**
- * Get the type of this specification,
+ * Gets the type of this specification,
* for example application
or library
in case of projects,
* and task
or server-middleware
in case of extensions
*
@@ -199,7 +217,7 @@ class Specification {
}
/**
- * Get the specification's generic version, as typically defined in a package.json
+ * Gets the specification's generic version, as typically defined in a package.json
*
* @public
* @returns {string} Project version
@@ -209,7 +227,7 @@ class Specification {
}
/**
- * Get the specification's file system path. This might not be POSIX-style on some platforms
+ * Gets the specification's file system path. This might not be POSIX-style on some platforms
*
* @public
* @returns {string} Project root path
@@ -220,7 +238,7 @@ class Specification {
/* === Resource Access === */
/**
- * Get a [ReaderCollection]{@link @ui5/fs/ReaderCollection} for the root directory of the specification.
+ * Gets a [ReaderCollection]{@link @ui5/fs/ReaderCollection} for the root directory of the specification.
* Resource readers always use POSIX-style
*
* @public
diff --git a/test/lib/graph/Workspace.js b/test/lib/graph/Workspace.js
index af79ca3d4..8515b0a69 100644
--- a/test/lib/graph/Workspace.js
+++ b/test/lib/graph/Workspace.js
@@ -82,6 +82,9 @@ test("Basic resolution", async (t) => {
t.is(await workspace.getModuleByNodeId("library.d"), libD,
"getModuleByNodeId returns correct module for library.d");
+ const modules = await workspace.getModules();
+ t.deepEqual(modules, [libD, libE], "getModules returns modules sorted by module ID");
+
t.deepEqual(Array.from(moduleIdMap.keys()).sort(), ["library.d", "library.e"], "Correct module ID keys");
moduleIdMap.forEach((value, key) => {
t.is(value, projectNameMap.get(key), `Same instance of module ${key} in both maps`);
diff --git a/test/lib/specifications/Specification.js b/test/lib/specifications/Specification.js
index 23c7fe1d6..3d6621c0e 100644
--- a/test/lib/specifications/Specification.js
+++ b/test/lib/specifications/Specification.js
@@ -66,6 +66,7 @@ test("Specification can't be instantiated", (t) => {
test("Instantiate a basic project", async (t) => {
const project = await Specification.create(t.context.basicProjectInput);
+ t.is(project.getId(), "application.a.id", "Returned correct ID");
t.is(project.getName(), "application.a", "Returned correct name");
t.is(project.getVersion(), "1.0.0", "Returned correct version");
t.is(project.getRootPath(), applicationAPath, "Returned correct project path");