skip to content

How do you customize the generated IDEA module with the `idea.module { ... }` DSL — for example excluding directories or marking extra source roots?

level: middleimportance: should knowfreq 30%

answer

  1. idea { module { excludeDirs } }
  2. sourceDirs / testSources extra roots
  3. scopes map COMPILE/RUNTIME/PROVIDED/TEST
  4. iml.withXml / beforeMerged / whenMerged
  5. affects generated .iml only

basics

~10 s

Use the idea { module { ... } } block. Common hooks: excludeDirs to exclude folders from the module, sourceDirs/testSources to add roots, and iml.withXml { ... } to tweak the raw .iml XML.

solid answer

~30 s

The `idea.module { ... }` DSL configures the per-project `.iml` file. The most-used settings are `excludeDirs` (a `Set<File>` of folders IDEA should ignore — e.g. build output or generated caches), `sourceDirs` and `testSources`/`testResources` to register extra source/test roots beyond the conventions, and `resourceDirs`. You can override `inheritOutputDirs`, set `outputDir`/`testOutputDir`, and tune `scopes` to control how Gradle configurations map to IDEA dependency scopes (COMPILE/RUNTIME/PROVIDED/TEST). For anything the typed DSL doesn't expose, `iml.withXml { it.asNode()... }` (or `beforeMerged`/`whenMerged` hooks) lets you mutate the generated XML model directly. Example: `idea { module { excludeDirs.add(file("node_modules")) } }`.

code

kotlin · 16 lines
kotlin
idea {
    module {
        // mark generated + tooling dirs as excluded
        excludeDirs.add(file("generated"))
        excludeDirs.add(file("node_modules"))

        // register an extra source root not covered by conventions
        sourceDirs.add(file("src/extra/java"))

        // remap a configuration into the PROVIDED scope
        scopes["PROVIDED"]?.get("plus")?.add(configurations["compileOnly"])

        // raw escape hatch
        iml.withXml { it.asNode() }
    }
}

go deeper

for a junior

Know the block exists and that excludeDirs removes folders from the module.

for a middle

Configure excludeDirs, extra source roots, and scopes; know the generated-files-only caveat.

for a senior

Explain the beforeMerged/whenMerged/withXml merge model and when each is appropriate.

for a principal

Decide whether file generation belongs in the workflow at all versus standardizing on native import, and govern the maintenance cost of withXml hacks.

## The `idea.module` block `idea.module { ... }` configures the `IdeaModule` model that the `ideaModule` task serializes into the `.iml`. It runs per Gradle project. ### Frequently used properties - **`excludeDirs: Set<File>`** — directories IDEA marks *excluded* (not indexed, not on the classpath). Typical adds: generated output, `node_modules`, scratch dirs. Note the plugin already excludes `.gradle` and `build` by default; you append with `excludeDirs.add(file("..."))` or reassign the whole set. - **`sourceDirs` / `testSources` / `resourceDirs` / `testResources`** — extra roots beyond the source-set conventions. (`testSources`/`testResources` are the modern `ConfigurableFileCollection` properties that replaced the older `testSourceDirs`/`testResourceDirs`.) - **`inheritOutputDirs`, `outputDir`, `testOutputDir`** — control compile-output locations IDEA records. - **`scopes`** — a `Map` keyed by IDEA scope (`COMPILE`, `RUNTIME`, `PROVIDED`, `TEST`) whose values are `plus`/`minus` lists of Gradle `Configuration`s, letting you change how dependencies are categorized in the module. - **`languageLevel`, `targetBytecodeVersion`, `jdkName`** — per-module overrides (usually set at project level instead). ### Raw-XML escape hatches When the typed DSL is insufficient, use the merge hooks on `iml`: ```kotlin idea { module { excludeDirs.add(file("generated")) iml { // mutate the in-memory model BEFORE Gradle merges its content beforeMerged { module -> /* ... */ } // mutate AFTER merge, e.g. add a custom facet whenMerged { module -> /* ... */ } withXml { provider -> provider.asNode() /* tweak DOM */ } } } } ``` `beforeMerged`/`whenMerged` operate on the typed `Module`/`Project` model; `withXml` operates on the serialized XML right before it's written. Use the typed hooks where possible and fall back to `withXml` only for settings the model doesn't represent. ### Important caveat These settings only affect **generated** `.iml` files. If your team relies on IDEA's **native Gradle import**, the IDE manages module structure itself and this DSL has no effect on what you see in the IDE.

  • What's the difference between `beforeMerged`, `whenMerged`, and `withXml`?
    `beforeMerged` mutates the typed model before Gradle merges existing file content; `whenMerged` mutates the typed model after merge; `withXml` mutates the final serialized XML right before writing. Prefer the typed hooks and use `withXml` only for things the model can't express.
  • Does configuring `idea.module.excludeDirs` change what you see when IDEA does a native Gradle import?
    No — it only affects the `.iml` produced by `./gradlew idea`. Under native import IDEA owns module structure, so the DSL has no visible effect there.

saying these in an interview costs you the question

  • Assuming `idea.module` settings apply to native Gradle import — they only affect generated files.
  • Forgetting `build` and `.gradle` are already excluded by default and reassigning `excludeDirs` in a way that drops them.
  • Reaching for `withXml` for everything instead of the typed `excludeDirs`/`scopes` DSL.

context