diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index 8dfbe6ad1e..4e54f3ed6f 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -20,6 +20,7 @@ Release with new features and bugfixes:
* https://github.com/devonfw/IDEasy/issues/1853[#1853]: Add ARM releases for VSCode on Mac
* https://github.com/devonfw/IDEasy/issues/797[#797]: Use system unzip on macOS to preserve symlinks in ZIP extraction
* https://github.com/devonfw/IDEasy/issues/1723[#1723]: Add commandlet for GitHub Copilot CLI
+* https://github.com/devonfw/IDEasy/issues/1685[#1685]: Add Nest CLI to IDEasy commandlets
The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/44?closed=1[milestone 2026.05.001].
diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java
index 58e49310e6..69b23e64b8 100644
--- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java
+++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java
@@ -42,6 +42,7 @@
import com.devonfw.tools.ide.tool.kubectl.KubeCtl;
import com.devonfw.tools.ide.tool.lazydocker.LazyDocker;
import com.devonfw.tools.ide.tool.mvn.Mvn;
+import com.devonfw.tools.ide.tool.nest.Nest;
import com.devonfw.tools.ide.tool.ng.Ng;
import com.devonfw.tools.ide.tool.node.Node;
import com.devonfw.tools.ide.tool.npm.Npm;
@@ -154,6 +155,7 @@ public CommandletManagerImpl(IdeContext context) {
add(new Go(context));
add(new Gui(context));
add(new SquirrelSql(context));
+ add(new Nest(context));
}
/**
diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/Tag.java b/cli/src/main/java/com/devonfw/tools/ide/common/Tag.java
index 6ae92c6e0b..559259eae4 100644
--- a/cli/src/main/java/com/devonfw/tools/ide/common/Tag.java
+++ b/cli/src/main/java/com/devonfw/tools/ide/common/Tag.java
@@ -329,6 +329,9 @@ public final class Tag {
/** {@link #Tag} for encryption. */
public static final Tag ENCRYPTION = create("encryption", CRYPTO);
+ /** {@link Tag} for Nest. */
+ public static final Tag NEST = create("nest", FRAMEWORK, false, new String[] { "nestjs", "nestcli" }, TYPE_SCRIPT);
+
private final String id;
private final Tag parent;
diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/PackageManagerBasedLocalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/PackageManagerBasedLocalToolCommandlet.java
index 10769d760e..126e538d7a 100644
--- a/cli/src/main/java/com/devonfw/tools/ide/tool/PackageManagerBasedLocalToolCommandlet.java
+++ b/cli/src/main/java/com/devonfw/tools/ide/tool/PackageManagerBasedLocalToolCommandlet.java
@@ -185,10 +185,18 @@ protected final void performToolInstallation(ToolInstallRequest request, Path in
PackageManagerRequest packageManagerRequest = new PackageManagerRequest(PackageManagerRequest.TYPE_INSTALL, getPackageName())
.setProcessContext(request.getProcessContext()).setVersion(request.getRequested().getResolvedVersion());
- runPackageManager(packageManagerRequest, true).failOnError();
+ runPackageManager(packageManagerRequest, isSkipInstallation()).failOnError();
this.installedVersion.invalidate();
}
+ /**
+ * @return {@code false} if the underlying {@link #getPackageManagerClass() package manager} should also be installed, {@code false} to skip that additional installation (e.g. to prevent infinite loop in case of cyclic
+ * dependencies between package manager based tools such as node and npm).
+ */
+ protected boolean isSkipInstallation() {
+ return false;
+ }
+
/**
* @return {@code true} if the tool can be uninstalled, {@code false} if not.
*/
diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/nest/Nest.java b/cli/src/main/java/com/devonfw/tools/ide/tool/nest/Nest.java
new file mode 100644
index 0000000000..30ad153999
--- /dev/null
+++ b/cli/src/main/java/com/devonfw/tools/ide/tool/nest/Nest.java
@@ -0,0 +1,30 @@
+package com.devonfw.tools.ide.tool.nest;
+
+import java.util.Set;
+
+import com.devonfw.tools.ide.common.Tag;
+import com.devonfw.tools.ide.context.IdeContext;
+import com.devonfw.tools.ide.tool.ToolCommandlet;
+import com.devonfw.tools.ide.tool.npm.NpmBasedCommandlet;
+
+/**
+ * {@link ToolCommandlet} for Nest CLI.
+ */
+public class Nest extends NpmBasedCommandlet {
+
+ /**
+ * The constructor.
+ *
+ * @param context the {@link IdeContext}.
+ */
+ public Nest(IdeContext context) {
+
+ super(context, "nest", Set.of(Tag.NEST, Tag.BUILD));
+ }
+
+ @Override
+ public String getPackageName() {
+
+ return "@nestjs/cli";
+ }
+}
diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/npm/Npm.java b/cli/src/main/java/com/devonfw/tools/ide/tool/npm/Npm.java
index 7dae4eb1c4..48079f01c2 100644
--- a/cli/src/main/java/com/devonfw/tools/ide/tool/npm/Npm.java
+++ b/cli/src/main/java/com/devonfw/tools/ide/tool/npm/Npm.java
@@ -46,6 +46,11 @@ public String getToolHelpArguments() {
return "help";
}
+ @Override
+ protected boolean isSkipInstallation() {
+ return true;
+ }
+
/**
* @return the {@link Path} to the npm user configuration file, creates the folder and configuration file if it was not existing.
*/
diff --git a/cli/src/main/resources/nls/Help.properties b/cli/src/main/resources/nls/Help.properties
index 1b8505d864..9f5dd711bd 100644
--- a/cli/src/main/resources/nls/Help.properties
+++ b/cli/src/main/resources/nls/Help.properties
@@ -81,6 +81,8 @@ cmd.list-versions=List the available versions of the selected tool.
cmd.list-versions.detail=To list all available versions of e.g. 'intellij' simply type: 'ide list-versions intellij'.
cmd.mvn=Tool commandlet for Maven (Build-Tool).
cmd.mvn.detail=Apache Maven is a build automation and dependency management tool for Java projects. Detailed documentation can be found at https://maven.apache.org/guides/index.html
+cmd.nest=Tool commandlet for Nest CLI.
+cmd.nest.detail=The Nest CLI is a command-line interface tool that helps you to initialize, develop, and maintain your Nest applications. Detailed documentation can be found at https://docs.nestjs.com/cli/overview
cmd.ng=Tool commandlet for Angular CLI.
cmd.ng.detail=Angular is a web framework that empowers developers to build fast, reliable applications. Detailed documentation can be found at https://angular.dev/overview/.
cmd.node=Tool commandlet for Node.js (JavaScript runtime).
diff --git a/cli/src/main/resources/nls/Help_de.properties b/cli/src/main/resources/nls/Help_de.properties
index dfcc22c285..a90b64bc66 100644
--- a/cli/src/main/resources/nls/Help_de.properties
+++ b/cli/src/main/resources/nls/Help_de.properties
@@ -81,6 +81,8 @@ cmd.list-versions=Listet die verfügbaren Versionen des selektierten Werkzeugs a
cmd.list-versions.detail=Um alle verfügbaren Versionen von z.B. 'intellij' aufzulisten, geben Sie einfach 'ide list-versions intellij' in die Konsole ein.
cmd.mvn=Werkzeug Kommando für Maven (Build-Werkzeug).
cmd.mvn.detail=Apache Maven ist ein Build-Automatisierungs- und Abhängigkeitsverwaltungstool für Java-Projekte. Detaillierte Dokumentation ist zu finden unter https://maven.apache.org/guides/index.html
+cmd.nest=Werkzeug Kommando für Nest CLI.
+cmd.nest.detail=Die Nest CLI ist ein Command‑Line‑Interface‑Tool zur Initialisierung, Entwicklung und Wartung von Nest‑Anwendungen. Detaillierte Dokumentation ist zu finden unter https://docs.nestjs.com/cli/overview
cmd.ng=Werkzeug Kommando für Angular CLI.
cmd.ng.detail=Angular ist ein web framework dass Entwicklern die Möglichkeit bietet, schnelle und zuverlässige Anwendungen zu entwickeln. Detaillierte Dokumentation ist zu finden unter https://angular.dev/overview/.
cmd.node=Werkzeug Kommando für Node.js (JavaScript Laufzeitumgebung).
diff --git a/cli/src/test/integration-tests/install-nest.sh b/cli/src/test/integration-tests/install-nest.sh
new file mode 100644
index 0000000000..ad5f9654ab
--- /dev/null
+++ b/cli/src/test/integration-tests/install-nest.sh
@@ -0,0 +1,13 @@
+echo "Running install nest integration test"
+ide -d install nest
+
+nest_location=""
+
+if doIsWindows
+then
+ nest_location=""
+else
+ nest_location="bin/"
+fi
+
+assertThat "${IDE_ROOT}/${TEST_PROJECT_NAME}/software/node/${nest_location}nest" exists
diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/nest/NestTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/nest/NestTest.java
new file mode 100644
index 0000000000..87f51b171f
--- /dev/null
+++ b/cli/src/test/java/com/devonfw/tools/ide/tool/nest/NestTest.java
@@ -0,0 +1,91 @@
+package com.devonfw.tools.ide.tool.nest;
+
+import org.junit.jupiter.api.Test;
+
+import com.devonfw.tools.ide.context.AbstractIdeContextTest;
+import com.devonfw.tools.ide.context.IdeTestContext;
+import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
+import com.github.tomakehurst.wiremock.junit5.WireMockTest;
+
+/**
+ * Test of {@link Nest}.
+ */
+@WireMockTest
+class NestTest extends AbstractIdeContextTest {
+
+ private static final String PROJECT_NEST = "nest";
+
+ /**
+ * Tests if the {@link Nest} install works correctly.
+ *
+ * @param wireMockRuntimeInfo wireMock server on a random port
+ */
+ @Test
+ void testNestInstall(WireMockRuntimeInfo wireMockRuntimeInfo) {
+
+ // arrange
+ IdeTestContext context = newContext(PROJECT_NEST, wireMockRuntimeInfo);
+ Nest commandlet = new Nest(context);
+
+ // act
+ commandlet.install();
+
+ // assert
+ checkInstallation(context);
+
+ }
+
+ /**
+ * Tests if the {@link Nest} install works correctly.
+ *
+ * @param wireMockRuntimeInfo wireMock server on a random port
+ */
+ @Test
+ void testNestUninstall(WireMockRuntimeInfo wireMockRuntimeInfo) {
+
+ // arrange
+ IdeTestContext context = newContext(PROJECT_NEST, wireMockRuntimeInfo);
+ Nest commandlet = new Nest(context);
+
+ // act I
+ commandlet.install();
+
+ // assert I
+ checkInstallation(context);
+
+ // act II
+ commandlet.uninstall();
+
+ // assert II
+ assertThat(context).logAtInfo().hasMessageContaining("npm uninstall -g @nestjs/cli");
+
+ assertThat(context).logAtSuccess().hasMessage("Successfully uninstalled nest");
+ }
+
+ /**
+ * Tests if {@link Nest} run works correctly.
+ *
+ * @param wireMockRuntimeInfo wireMock server on a random port
+ */
+ @Test
+ void testNestRun(WireMockRuntimeInfo wireMockRuntimeInfo) {
+
+ // arrange
+ IdeTestContext context = newContext(PROJECT_NEST, wireMockRuntimeInfo);
+ Nest commandlet = new Nest(context);
+ commandlet.arguments.setValue("--version");
+
+ // act
+ commandlet.run();
+
+ // assert
+ assertThat(context).logAtInfo().hasMessageContaining("nest --version");
+ }
+
+ private void checkInstallation(IdeTestContext context) {
+
+ assertThat(context).logAtInfo().hasMessageContaining("npm install -gf @nestjs/cli@11.0.21");
+
+ assertThat(context).logAtSuccess().hasMessageContaining("Successfully installed nest in version 11.0.21");
+ }
+}
diff --git a/cli/src/test/resources/ide-projects/nest/_ide/urls/node/node/v18.19.1/urls b/cli/src/test/resources/ide-projects/nest/_ide/urls/node/node/v18.19.1/urls
new file mode 100644
index 0000000000..a07d5a1205
--- /dev/null
+++ b/cli/src/test/resources/ide-projects/nest/_ide/urls/node/node/v18.19.1/urls
@@ -0,0 +1 @@
+${testbaseurl}/download/node/node/v18.19.1/node-v18.19.1.tgz
diff --git a/cli/src/test/resources/ide-projects/nest/_ide/urls/npm/npm/dependencies.json b/cli/src/test/resources/ide-projects/nest/_ide/urls/npm/npm/dependencies.json
new file mode 100644
index 0000000000..b84a1b86c9
--- /dev/null
+++ b/cli/src/test/resources/ide-projects/nest/_ide/urls/npm/npm/dependencies.json
@@ -0,0 +1,8 @@
+{
+ "[9.0, 10.0)": [
+ {
+ "tool": "node",
+ "versionRange": "[18.0.0,)"
+ }
+ ]
+}
diff --git a/cli/src/test/resources/ide-projects/nest/project/home/.ide/ide.properties b/cli/src/test/resources/ide-projects/nest/project/home/.ide/ide.properties
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/cli/src/test/resources/ide-projects/nest/project/settings/ide.properties b/cli/src/test/resources/ide-projects/nest/project/settings/ide.properties
new file mode 100644
index 0000000000..77fba82c0e
--- /dev/null
+++ b/cli/src/test/resources/ide-projects/nest/project/settings/ide.properties
@@ -0,0 +1,3 @@
+NODE_VERSION=v18.19.1
+NPM_VERSION=9.9.2
+NEST_VERSION=11.0.21
diff --git a/cli/src/test/resources/ide-projects/nest/project/workspaces/main/.gitkeep b/cli/src/test/resources/ide-projects/nest/project/workspaces/main/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/cli/src/test/resources/ide-projects/nest/repository/node/node/default/nest b/cli/src/test/resources/ide-projects/nest/repository/node/node/default/nest
new file mode 100755
index 0000000000..6a41fc7994
--- /dev/null
+++ b/cli/src/test/resources/ide-projects/nest/repository/node/node/default/nest
@@ -0,0 +1,2 @@
+#!/bin/bash
+echo "nest $*"
diff --git a/cli/src/test/resources/ide-projects/nest/repository/node/node/default/npm b/cli/src/test/resources/ide-projects/nest/repository/node/node/default/npm
new file mode 100755
index 0000000000..ee633ea32e
--- /dev/null
+++ b/cli/src/test/resources/ide-projects/nest/repository/node/node/default/npm
@@ -0,0 +1,6 @@
+#!/bin/bash
+if [ "$1" == "--version" ]; then
+ echo "9.9.2"
+ exit
+fi
+echo "npm $*"
diff --git a/cli/src/test/resources/ide-projects/nest/repository/node/node/default/npx b/cli/src/test/resources/ide-projects/nest/repository/node/node/default/npx
new file mode 100755
index 0000000000..0723c2041b
--- /dev/null
+++ b/cli/src/test/resources/ide-projects/nest/repository/node/node/default/npx
@@ -0,0 +1,2 @@
+#!/bin/bash
+echo "npx $*"
diff --git a/cli/src/test/resources/ide-projects/nest/repository/npmjs/@nest/cli.json b/cli/src/test/resources/ide-projects/nest/repository/npmjs/@nest/cli.json
new file mode 100644
index 0000000000..fd621c4c96
--- /dev/null
+++ b/cli/src/test/resources/ide-projects/nest/repository/npmjs/@nest/cli.json
@@ -0,0 +1,10 @@
+{
+ "versions": {
+ "11.0.21": {
+ "version": "11.0.21",
+ "dist": {
+ "tarball": "${testbaseurl}/@nestjs/-/cli-11.0.21.tgz"
+ }
+ }
+ }
+}
diff --git a/documentation/LICENSE.adoc b/documentation/LICENSE.adoc
index a84f3c7ccc..c1c5c60664 100644
--- a/documentation/LICENSE.adoc
+++ b/documentation/LICENSE.adoc
@@ -103,6 +103,7 @@ The column `inclusion` indicates the way the component is included:
|https://docs.astral.sh/uv/[uv] |Optional|https://docs.astral.sh/uv/reference/policies/license/[Apache 2.0]
|https://github.com/openjdk/jfx[OpenJFX] |Optional|https://github.com/openjdk/jfx/blob/master/LICENSE[GPLv2] (with the “Classpath” Exception)
|https://squirrel-sql.sourceforge.io/[SQuirreL SQL Client]|Optional|https://github.com/squirrel-sql-client/squirrel-sql-stable-releases/blob/main/LICENSE[LGPL 2.1]
+|https://docs.nestjs.com/cli/overview[NestJS CLI] |Optional|https://github.com/nestjs/nest-cli/blob/master/LICENSE[MIT License]
|===
== Apache Software License - Version 2.0
diff --git a/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java
index 5dc08f8e7a..fcadcb2121 100644
--- a/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java
+++ b/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java
@@ -4,8 +4,6 @@
import java.time.Instant;
import java.util.List;
-import com.devonfw.tools.ide.url.tool.java.JavaAzulUrlUpdater;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,6 +30,7 @@
import com.devonfw.tools.ide.url.tool.helm.HelmUrlUpdater;
import com.devonfw.tools.ide.url.tool.intellij.IntellijUrlUpdater;
import com.devonfw.tools.ide.url.tool.jasypt.JasyptUrlUpdater;
+import com.devonfw.tools.ide.url.tool.java.JavaAzulUrlUpdater;
import com.devonfw.tools.ide.url.tool.java.JavaUrlUpdater;
import com.devonfw.tools.ide.url.tool.jenkins.JenkinsUrlUpdater;
import com.devonfw.tools.ide.url.tool.jmc.JmcUrlUpdater;
@@ -69,7 +68,8 @@ public class UpdateManager extends AbstractProcessorWithTimeout {
private final UrlFinalReport urlFinalReport;
private final List updaters = List.of(
- new AndroidStudioUrlUpdater(), new AwsUrlUpdater(), new AzureUrlUpdater(), new CopilotUrlUpdater(), new CorepackUrlUpdater(), new DockerDesktopUrlUpdater(),
+ new AndroidStudioUrlUpdater(), new AwsUrlUpdater(), new AzureUrlUpdater(), new CopilotUrlUpdater(), new CorepackUrlUpdater(),
+ new DockerDesktopUrlUpdater(),
new DotNetUrlUpdater(),
new EclipseCppUrlUpdater(), new EclipseJeeUrlUpdater(), new EclipseJavaUrlUpdater(), new GCloudUrlUpdater(),
new GcViewerUrlUpdater(), new GhUrlUpdater(), new GoUrlUpdater(), new GraalVmCommunityUpdater(), new GraalVmOracleUrlUpdater(),
@@ -77,7 +77,8 @@ public class UpdateManager extends AbstractProcessorWithTimeout {
new JavaUrlUpdater(), new JavaAzulUrlUpdater(), new JenkinsUrlUpdater(), new JmcUrlUpdater(), new KotlincUrlUpdater(),
new KotlincNativeUrlUpdater(), new LazyDockerUrlUpdater(), new MvnUrlUpdater(),
new NgUrlUpdater(), new NodeUrlUpdater(), new NpmUrlUpdater(), new OcUrlUpdater(), new PgAdminUrlUpdater(), new PipUrlUpdater(), new PycharmUrlUpdater(),
- new PythonUrlUpdater(), new QuarkusUrlUpdater(), new RustUrlUpdater(), new DockerRancherDesktopUrlUpdater(), new SonarUrlUpdater(), new SquirrelSqlUrlUpdater(),
+ new PythonUrlUpdater(), new QuarkusUrlUpdater(), new RustUrlUpdater(), new DockerRancherDesktopUrlUpdater(), new SonarUrlUpdater(),
+ new SquirrelSqlUrlUpdater(),
new TerraformUrlUpdater(), new TomcatUrlUpdater(), new UvUrlUpdater(), new VsCodeUrlUpdater());
/**