npm package: @jnxplus/nx-maven
Description: [![npm version](https://badge.fury.io/js/@jnxplus%2Fnx-maven.svg)](https://badge.fury.io/js/@jnxplus%2Fnx-maven)
Author: Khalil LAGRIDA
Latest version: 2.3.0

# @jnxplus/nx-maven

[![npm version](https://badge.fury.io/js/@jnxplus%2Fnx-maven.svg)](https://badge.fury.io/js/@jnxplus%2Fnx-maven)

This plugin adds Maven multi-module capabilities to Nx workspace.

## Quick Start

Get started with nx-maven in 5 steps:

```bash
# 1. Install the plugin
npm install --save-dev @jnxplus/nx-maven

# 2. Initialize workspace with Maven support (Java 17 + Spring Boot Parent POM)
nx generate @jnxplus/nx-maven:init --javaVersion 17 --dependencyManagement spring-boot-parent-pom

# 3. Generate a library
nx generate @jnxplus/nx-maven:library my-lib --framework spring-boot --directory libs

# 4. Generate an application that uses the library
nx generate @jnxplus/nx-maven:application my-app --framework spring-boot --projects my-lib --directory apps

# 5. Serve the application
nx serve my-app
```

## Supported versions

| @jnxplus/nx-maven | Nx            | Spring Boot | Quarkus | Micronaut |
| ----------------- | ------------- | ----------- | ------- | --------- |
| 2.x.x             | >= 22         | 3.x.x       | 3.x.x   | 4.x.x     |
| 1.x.x             | >= 19 & <= 21 | 3.x.x       | 3.x.x   | 4.x.x     |
| 0.x.x             | >= 17 & <= 18 | 3.x.x       | 3.x.x   | 4.x.x     |

## Getting Started

### 0. Prerequisites

`@jnxplus/nx-maven` requires a Java 17 or higher Runtime Environment and the current Long Term Support (LTS) version of node.js.

### 1. Install the plugin

In the Nx workspace root folder, run this command to install the plugin :

```bash
npm install --save-dev @jnxplus/nx-maven
```

### 2. Init workspace with Maven support

The following command adds Maven support (Maven wrapper and config files) to the workspace. This only needs to be performed once per workspace.

```bash
nx generate @jnxplus/nx-maven:init
```

You will be prompted for:

- **Java version** (default: none) - Options: none, 17, 21, or 25
- **Dependency management strategy** (default: none) - Options:
  - `spring-boot-parent-pom` - Spring Boot Parent POM
  - `spring-boot-bom` - Spring Boot BOM (Bill of Materials)
  - `quarkus-bom` - Quarkus BOM
  - `micronaut-parent-pom` - Micronaut Parent POM
  - `micronaut-bom` - Micronaut BOM
  - `none` - Configure manually later in a parent project (for advanced use cases)

**Additional options:**

- `--mavenRootDirectory` - Subdirectory for Maven files (default: workspace root). See [Maven Root Directory](#important-maven-root-directory) section.
- `--aggregatorProjectGroupId` - GroupId for root aggregator project (default: com.example)
- `--aggregatorProjectName` - Name for root aggregator project (default: root-aggregator-project)
- `--aggregatorProjectVersion` - Version for root aggregator project (default: 0.0.1-SNAPSHOT)
- `--localRepoRelativePath` - Path to Maven local repository where dependencies are stored, relative to mavenRootDirectory (default: .m2/repository). Must be a subfolder within the workspace so Nx can cache and restore dependencies between builds
- `--skipWrapper` - Skip generating Maven wrapper (default: false)
- `--formatter` - Code formatter to use: prettier or none (default: prettier)

#### Important: Maven Root Directory

By default, Maven files (wrapper, config, and projects) are placed at the workspace root. However, you can specify a subdirectory using the `--mavenRootDirectory` option:

```bash
nx generate @jnxplus/nx-maven:init --mavenRootDirectory maven
```

This creates the following structure:

```
workspace-root/
├── maven/                   # Maven root directory
│   ├── .mvn/                # Maven wrapper config
│   ├── mvnw, mvnw.cmd       # Maven wrapper scripts
│   ├── pom.xml              # Root aggregator POM
│   ├── .m2/repository/      # Local Maven repository
│   └── my-app/              # Your projects
│       └── pom.xml
├── apps/                    # Other Nx apps (non-Maven)
├── libs/                    # Other Nx libs (non-Maven)
└── package.json
```

**When to use `mavenRootDirectory`:**

- ✅ **Monorepo organization** - Keep Maven projects isolated in their own directory
- ✅ **Better Nx caching** - Separating Maven files improves Nx cache calculations and project graph performance

**When to skip `mavenRootDirectory` (use root):**

- ✅ **Simpler structure** - Fewer nested directories
- ✅ **Hybrid workspaces** - Mix Maven projects with other technologies (Node.js, Python, etc.) at the root level

**Note:** Once set during init, the `mavenRootDirectory` should remain consistent for all Maven projects in the workspace.

### 3. Generate a parent project (optional)

Parent projects help organize your applications and libraries with shared dependency management. Use parent projects when you need custom dependency management, want to organize projects into logical groups, or need to support multiple frameworks in your workspace.

```bash
nx generate @jnxplus/nx-maven:parent-project my-parent-project
```

Key options:

- `--javaVersion` - Java version (17, 21, 25, or none)
- `--dependencyManagement` - Dependency management strategy (same options as init)
- `--language` - Language for sub-projects: java, kotlin, or java-kotlin
- `--directory` - Directory where the project will be created (default: `libs`)
- `--parentProject` - Parent project to inherit from (for nested parent projects)
- `--aggregatorProject` - Aggregator project that manages a group of submodules

### 4. Generate applications and libraries

#### Generate an application

```bash
nx generate @jnxplus/nx-maven:application my-app
```

Key options:

- `--framework` - Framework to use: spring-boot, quarkus, micronaut, or none
- `--language` - Language: java or kotlin (default: java)
- `--parentProject` - Parent project to use (uses root aggregator project if not specified)
- `--port` - Server port for the application
- `--packaging` - Packaging type: jar or war (default: jar)
- `--minimal` - Generate minimal application without starter code
- `--groupId` - Maven groupId (default: com.example)
- `--projectVersion` - Maven version (default: 0.0.1-SNAPSHOT)
- `--directory` - Directory where the project will be created (default: `apps`, e.g., `backend` creates at `backend/my-app`, `apps/backend` creates at `apps/backend/my-app`)
- `--simpleName` - Don't include the directory in the project name (default: true)
- `--simplePackageName` - Don't include the directory in the package name (default: true)
- `--tags` - Tags for the project (comma-separated)

#### Generate a library

```bash
nx generate @jnxplus/nx-maven:library my-lib
```

Key options:

- `--framework` - Framework to use: spring-boot, quarkus, micronaut, or none
- `--language` - Language: java or kotlin (default: java)
- `--parentProject` - Parent project to use (uses root aggregator project if not specified)
- `--projects` - Projects that will use this library (comma-separated)
- `--skipStarterCode` - Skip generating starter code
- `--groupId` - Maven groupId (default: com.example)
- `--projectVersion` - Maven version (default: 0.0.1-SNAPSHOT)
- `--directory` - Directory where the project will be created (default: `libs`, e.g., `backend` creates at `backend/my-lib`, `libs/backend` creates at `libs/backend/my-lib`)
- `--simpleName` - Don't include the directory in the project name (default: true)
- `--simplePackageName` - Don't include the directory in the package name (default: true)
- `--tags` - Tags for the project (comma-separated)

**Example with directory:**

```bash
# Creates project at: backend/my-lib
# Project name (with simpleName=true): my-lib
# Package name (with simplePackageName=true): com.example.mylib
nx generate @jnxplus/nx-maven:library my-lib --directory backend

# Creates project at: backend/my-lib
# Project name (with simpleName=false): backend-my-lib
# Package name (with simplePackageName=false): com.example.backend.mylib
nx generate @jnxplus/nx-maven:library my-lib --directory backend --simpleName false --simplePackageName false
```

### 5. Common tasks

| Action                               | Command                                         |
| ------------------------------------ | ----------------------------------------------- |
| Build a project                      | `nx build my-project`                           |
| Serve an application                 | `nx serve my-app`                               |
| Test a project                       | `nx test my-project`                            |
| Build a Docker image                 | `nx build-image my-app`                         |
| Run custom Maven task                | `nx run-task my-project --task="clean install"` |
| Format a Java project                | `nx format --projects my-project`               |
| Visualize project's dependency graph | `nx graph`                                      |

### 6. Executors

nx-maven primarily uses the `run-task` executor for all Maven operations. Most targets (build, test, serve, etc.) internally use `run-task` to execute Maven commands.

#### run-task

Execute arbitrary Maven tasks on a project:

```bash
nx run-task my-project --task="clean install"
```

**Options:**

- `--task` (required) - Maven task(s) to execute. Can be a string or array of strings
- `--outputDirLocalRepo` - Sub-directory in Maven local repository where artifacts from install phase will be placed
- `--skipProject` - Skip specifying the project with `-pl :project` flag
- `--cwd` - Working directory for the command. Can be relative (to Maven root) or absolute
- `--skipExecutor` - Skip executor execution (useful for conditional runs)

**Examples:**

```bash
# Single task
nx run-task my-app --task="clean package"

# Multiple tasks
nx run-task my-app --task="clean" --task="test" --task="package"

# With custom working directory
nx run-task my-app --task="compile" --cwd="custom-path"

# Skip project specification (run from Maven root)
nx run-task my-app --task="dependency:tree" --skipProject
```

#### quarkus-build-image

Build a Docker image for Quarkus applications:

```bash
nx build-image my-quarkus-app
```

**Options:**

- `--imageType` - Image type to build (default: jvm). Options: jvm, native, etc.
- `--imageNamePrefix` - Image name prefix (default: quarkus)
- `--imageNameSuffix` - Image name suffix

**Examples:**

```bash
# Build JVM image (default)
nx build-image my-quarkus-app

# Build native image
nx build-image my-quarkus-app --imageType=native

# Custom image name
nx build-image my-quarkus-app --imageNamePrefix=myapp --imageNameSuffix=latest
```

**Note:** For Spring Boot and Micronaut applications, the build-image target uses the `run-task` executor with framework-specific commands:

- Spring Boot: `spring-boot:build-image`
- Micronaut: `package -Dpackaging=docker`

### 7. Plugin configuration

The init command configures the plugin in your `nx.json` file. You can customize these options to fit your workspace needs.

**Example configuration with default values:**

```json
{
  "plugins": [
    {
      "plugin": "@jnxplus/nx-maven",
      "options": {
        "mavenRootDirectory": "",
        "localRepoRelativePath": ".m2/repository",
        "buildTargetName": "build",
        "testTargetName": "test",
        "serveTargetName": "serve",
        "integrationTestTargetName": "integration-test",
        "buildImageTargetName": "build-image",
        "skipAggregatorProjectLinking": false,
        "skipProjectWithoutProjectJson": false
      }
    }
  ]
}
```

**Available options:**

- `mavenRootDirectory` - Subdirectory for Maven files (default: workspace root)
- `localRepoRelativePath` - Path to Maven local repository (default: .m2/repository)
- `buildTargetName` - Name for the build target (default: build)
- `testTargetName` - Name for the test target (default: test)
- `serveTargetName` - Name for the serve target (default: serve)
- `integrationTestTargetName` - Name for the integration test target (default: integration-test)
- `buildImageTargetName` - Name for the build image target (default: build-image)
- `skipAggregatorProjectLinking` - Skip linking aggregator projects in the dependency graph. Enable this when aggregator projects only contain `<modules>` declarations and all configuration is handled by parent projects. This improves Nx graph performance by reducing unnecessary dependency links. See [Understanding parent projects vs aggregator projects](#10-understanding-parent-projects-vs-aggregator-projects) for more details (default: false)
- `skipProjectWithoutProjectJson` - Skip projects that don't have a project.json file (default: false)

### 8. Environment variables

You can customize nx-maven behavior using environment variables:

#### NX_MAVEN_CLI

Controls which Maven executable to use. Accepts: `mvnw`, `mvn`, or `mvnd`.

**Examples:**

```bash
# Linux/macOS
export NX_MAVEN_CLI=mvn
nx build my-app

# Windows (PowerShell)
$env:NX_MAVEN_CLI='mvnd'
nx build my-app

# Windows (CMD)
set NX_MAVEN_CLI=mvn
nx build my-app
```

**Default behavior (when not set):**

- Uses `mvnw` (Maven wrapper) if it exists in the workspace
- Falls back to `mvn` if wrapper doesn't exist

#### NX_MAVEN_CLI_OPTS

Pass additional arguments to Maven commands globally. Useful for CI/CD environments or consistent build settings.

**Examples:**

```bash
# Linux/macOS - disable transfer progress and enable verbose plugin validation
export NX_MAVEN_CLI_OPTS='--no-transfer-progress -Dmaven.plugin.validation=VERBOSE'
nx build my-app

# Windows (PowerShell) - run in batch mode
$env:NX_MAVEN_CLI_OPTS='--batch-mode'
nx test my-app

# Windows (CMD) - use offline mode
set NX_MAVEN_CLI_OPTS=--offline
nx build my-app
```

**Common options:**

- `--no-transfer-progress` - Disable download progress output (useful in CI)
- `--batch-mode` - Run in non-interactive mode
- `--offline` - Work offline (use cached dependencies only)
- `-Dmaven.plugin.validation=VERBOSE` - Enable verbose plugin validation

#### Using a .env file

Create a `.env` file in your workspace root to persist environment variables across sessions:

```bash
# .env file example
NX_MAVEN_CLI=mvnd
NX_MAVEN_CLI_OPTS=--no-transfer-progress --batch-mode
```

**Note:** The `.env` file is automatically loaded by Nx. Variables defined here will be available to all nx-maven commands.

### 9. Visualizing the project graph

Nx provides a powerful project graph visualization to understand dependencies between your Maven projects:

```bash
nx graph
```

This opens an interactive view showing:

- **Project dependencies** - How your applications and libraries depend on each other
- **Build order** - Understanding which projects need to be built first
- **Impact analysis** - See what projects are affected by changes

**Useful graph commands:**

```bash
# View the full project graph
nx graph

# Show what's affected by changes
nx graph --affected

# Focus on a specific project and its dependencies
nx graph --focus=my-app

# Show only projects that depend on a specific project
nx graph --exclude=*,!tag:my-tag
```

**How nx-maven builds the graph:**

- **Maven dependencies** - Extracted from `pom.xml` files (`<dependencies>` sections)
- **Profile dependencies** - Dependencies defined within Maven profiles (`<profiles>` → `<dependencies>`)
- **Plugin dependencies** - Dependencies used by Maven plugins (`<build>` → `<plugins>` → `<dependencies>`). Note: Plugin dependencies can sometimes create cyclic dependency issues in the graph
- **Parent projects** - Child projects depend on their parent (`<parent>` section)
- **Aggregator projects** - Optional linking based on `<modules>` (controlled by `skipAggregatorProjectLinking`)

**Limitations:**

- Profile dependencies are always included in the graph, regardless of whether the profile is active. This ensures the dependency graph remains consistent but may show dependencies that aren't used in your current build configuration.

**Skipping projects:**

You can exclude certain projects from the graph by setting `skipProjectWithoutProjectJson: true` in your plugin configuration. This will skip any Maven projects that don't have a `project.json` file, which is useful for ignoring Maven modules that aren't managed by Nx.

This integration allows Nx to:

- Run only affected tests when you make changes
- Build projects in the correct order
- Cache and distribute builds efficiently

### 10. Understanding parent projects vs aggregator projects

**Why separate them?** In nx-maven, keeping parent projects (configuration) separate from aggregator projects (module lists) improves Nx graph performance. Parent projects stay stable, aggregators only change when adding/removing projects.

#### Parent Project (`--parentProject`)

**What it does:** Child projects inherit configuration and dependency management.

**Contains:**

- Dependency versions (`<dependencyManagement>`)
- Plugin configurations
- Java version and properties
- Framework configurations (Spring Boot, Quarkus, Micronaut)

**Example `pom.xml`:**

```xml
<parent>
  <groupId>com.example</groupId>
  <artifactId>my-parent</artifactId>
  <version>0.0.1-SNAPSHOT</version>
</parent>
```

#### Aggregator Project (`--aggregatorProject`)

**What it does:** Lists submodules for coordinated Maven builds.

**Contains:**

- Only `<modules>` declarations
- No configuration or dependency management (use parent projects for that)

**Example `pom.xml`:**

```xml
<modules>
  <module>my-app</module>
  <module>my-lib</module>
</modules>
```

#### Comparison

| Aspect       | Parent Project             | Aggregator Project              |
| ------------ | -------------------------- | ------------------------------- |
| Purpose      | Configuration inheritance  | Build coordination              |
| Direction    | Bottom-up (child → parent) | Top-down (aggregator → modules) |
| Changes when | Configuration updates      | Projects added/removed          |

#### Patterns

**1. Default (root as parent and aggregator):**

```bash
# Generate applications and libraries directly
# They inherit from root pom.xml and are listed there as modules
nx generate @jnxplus/nx-maven:application my-app --framework spring-boot
nx generate @jnxplus/nx-maven:library my-lib --framework spring-boot
```

All projects inherit configuration from and are aggregated by the root `pom.xml`.

**2. Separate parent and aggregator (recommended for advanced performance):**

```bash
# Create parent for configuration
nx generate @jnxplus/nx-maven:parent-project shared-config --javaVersion 17

# Create aggregator (modules only, no config)
nx generate @jnxplus/nx-maven:parent-project apps-aggregator --javaVersion none --dependencyManagement none

# Projects inherit from parent, listed in aggregator
nx generate @jnxplus/nx-maven:application app1 --parentProject shared-config --aggregatorProject apps-aggregator
```

**Tip:** When using this pattern, enable `skipAggregatorProjectLinking: true` in your `nx.json` plugin options to optimize Nx graph performance since aggregators only contain module lists.

### 11. Project tagging

All projects generated by nx-maven are automatically tagged with `nx-maven`. This allows you to:

**Target Maven projects specifically:**

```bash
# Run tests only on Maven projects
nx run-many -t test --projects=tag:nx-maven

# Build all Maven projects
nx run-many -t build --projects=tag:nx-maven

# Lint only Maven projects
nx run-many -t lint --projects=tag:nx-maven
```

**Filter in the project graph:**

```bash
# Show only Maven projects in the graph
nx graph --projects=tag:nx-maven
```

**Custom tags:**

You can add additional tags when generating projects using the `--tags` option:

```bash
# Add custom tags
nx generate @jnxplus/nx-maven:application my-app --tags=backend,api

# Projects will have both auto and custom tags: ['nx-maven', 'backend', 'api']
```

### 12. Version management

nx-maven handles both static and property-based versions from `pom.xml` files.

#### Static versions

```xml
<version>0.0.1-SNAPSHOT</version>
```

Static versions are used directly for dependency resolution and project graph construction.

#### Property-based versions

nx-maven supports Maven's [CI-friendly versions](https://maven.apache.org/guides/mini/guide-maven-ci-friendly.html) using properties like `${revision}`, `${sha1}`, or `${changelist}`:

```xml
<version>${revision}</version>

<properties>
  <revision>1.0.0-SNAPSHOT</revision>
</properties>
```

**Version resolution process:**

When nx-maven encounters a property-based version, it resolves it in this order:

1. **Project properties** - Check `<properties>` in the current project's `pom.xml`
2. **Parent properties** - Recursively check parent project properties
3. **Maven evaluation** - As a fallback, use `mvn help:evaluate` (with performance warning)

**Example with CI-friendly versions:**

```xml
<!-- Parent pom.xml -->
<groupId>com.example</groupId>
<artifactId>parent</artifactId>
<version>${revision}</version>

<properties>
  <revision>1.0.0-SNAPSHOT</revision>
</properties>

<!-- Child pom.xml -->
<parent>
  <groupId>com.example</groupId>
  <artifactId>parent</artifactId>
  <version>${revision}</version>
</parent>
<!-- Child inherits revision property from parent -->
```

**Limitations:**

- nx-maven resolves versions statically when building the project graph, not at runtime. If you override versions at build time (e.g., `mvn clean install -Drevision=1.2.3`), the graph will still use the version defined in `<properties>`.

**Note:** If nx-maven cannot resolve a version using properties (e.g., `${project.parent.version}`), it will fall back to `mvn help:evaluate`, which is slower. If you encounter performance issues, please [open an issue](https://github.com/khalilou88/jnxplus/issues).

### 13. Other generators

#### Preset

The preset generator is used when creating a new Nx workspace with Maven support from scratch:

```bash
npx create-nx-workspace@latest my-workspace --preset=@jnxplus/nx-maven
```

This combines workspace creation and Maven initialization in one step.

#### Wrapper

Update or add the Maven wrapper to your workspace:

```bash
nx generate @jnxplus/nx-maven:wrapper
```

**Options:**

- `--skipGitignore` - Don't add Maven Wrapper to .gitignore (default: false)
- `--skipFormat` - Skip formatting files (default: false)

Use this generator to:

- Update to the latest Maven wrapper version
- Add the wrapper if it was skipped during init (`--skipWrapper`)
- Restore the wrapper if it was accidentally deleted

## License

MIT © 2021-2026 Khalil LAGRIDA