How do you customize the generated IDEA module with the `idea.module { ... }` DSL — for example excluding directories or marking extra source roots?
answer
- idea { module { excludeDirs } }
- sourceDirs / testSources extra roots
- scopes map COMPILE/RUNTIME/PROVIDED/TEST
- iml.withXml / beforeMerged / whenMerged
- affects generated .iml only
basics
~10 sUse 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 sThe `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 linesidea {
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
Know the block exists and that excludeDirs removes folders from the module.
Configure excludeDirs, extra source roots, and scopes; know the generated-files-only caveat.
Explain the beforeMerged/whenMerged/withXml merge model and when each is appropriate.
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.