Carl Taylor
12/12/2025, 3:38 AMui is the module that uses the generated source code from build-config after analyzing the classes of the
web module:
The problem is that I cannot use the templating variables to CHOOSE which module to analyze and which module to register
resources with.
The naive approach of using the auto-completed variables runs into a loop error (which makes sense):
# build-config/plugin.yaml
tasks:
generate:
action: !generateTypescriptApiClient
generatedSourceDir: ${taskOutputDir}
classpaths:
# Understandable why I cannot use the module templating variables here, as they are not resolved yet.
- ${module.compileClasspath}
- ${module.runtimeClasspath}
markOutputsAs:
- path: ${action.generatedSourceDir}
kind: jvm-resources
However, I would expect overriding to work:
# ui/module.yaml -- Does not work
plugins:
build-config:
enabled: true
classpaths:
- ${web.compileClasspath}
- ${web.runtimeClasspath}
This is what I get with the above:
➤ ./amper --version
JetBrains Amper version 0.9.2 (80094bb, 2025-12-11)
➤ ./amper build
00:01.204 INFO Processing local plugin schema for [build-config]...
00:03.420 ERROR /Users/nthalk/local/src/iodesystems/agent/ui/module.yaml:24:5: Unknown property classpaths
00:03.515 ERROR Task dependency loop detected:
1. task `generate` in module `ui` from plugin `build-config` (*)
╰───> requests classpath resolution that includes the results from,
requests classpath resolution that includes the results from
2. compilation of module `ui` <─────────────────────────────────────╯
╰───> needs resources from ───────────╮
3. resource generation for module `ui` <─╯
╰───> includes the directory `<project-build-dir>/tasks/_ui_generate@build-config` generated by
4. task `generate` in module `ui` from plugin `build-config` (*) <───────────────────────────────╯
╰─ Related configuration elements that may have caused the loop:
╰─ /Users/nthalk/local/src/iodesystems/agent/build-config/plugin.yaml:6:11
╰─ /Users/nthalk/local/src/iodesystems/agent/build-config/plugin.yaml:7:11
╰─ /Users/nthalk/local/src/iodesystems/agent/build-config/plugin.yaml:9:9
When I replace classpaths in build-config/plugin.yaml with [] and try to specify module scoped variables in
ui/module.yaml, I get:
# See <https://amper.org/latest/user-guide/plugins/quick-start/#adding-plugin-settings>
...
plugins:
build-config:
enabled: true
classpaths:
- ${web.compileClasspath}
- ${web.runtimeClasspath}
➤ ./amper build
00:01.170 INFO Processing local plugin schema for [build-config]...
00:03.422 ERROR /Users/nthalk/local/src/iodesystems/agent/ui/module.yaml:24:5: Unknown property classpaths
ERROR: failed to read Amper model, refer to the errors above
Is there any documentation about using one module to generate resources for another module via a plugin? I assume this is a common case.Carl Taylor
12/12/2025, 4:23 AMAnton Prokhorov
12/12/2025, 8:58 AMHowever, I would expect overriding to work:This kinda makes sense that you think that might work But I'd still would rather learn from you, where it comes from, that would be very insightful data to refine our expectations about what is intuitive and what isn't I suspect you have a dependency loop because module's
compileClasspath requires resources to be processed already, and you mark output of the action as jvm-resources
@Fedor Ihnatkevich please confirm or disprove me
considering your use case, I don't think it's supported now: even if you create a new module with the dependency to web and use module's compileClasspath and generate some artifact, other module couldn't consume it from ui module as a separate classpath, so either it would be part of the whole classpath (including web which might be a workaround) or there is no way... for now at least, I might be wrong, so
cc @Fedor Ihnatkevich
In any way, it would be great to understand your use case better
could you please elaborate a bit?
what do you mean by typescript generator metadata?
what is your web app? is it a serverside? what your generateTypescriptApiClient does exactly?
If the artifact of your generation isn't consumed by Amper in any way, then, I believe you need to apply plugin to web module directly and don't mark output, I think that should be enoughFedor Ihnatkevich
12/12/2025, 9:34 AMAdding plugin settings (this one). Are you sure you have set your settings interface in plugin's module.yaml as shown in the docs:
pluginInfo:
settingsClass: com.example.Settings
Then you have to reference these settings via ${pluginSettings.classpaths} in your task, like classpaths: ${pluginSettings.classpaths} or better just pass the whole settings object into the task action directly and reference it like settings: ${pluginSettings}.
Configuring tasks directly from the module.yaml is not possible - tasks are implementation detail; the plugin configuration has to go through the pluginSettings.
Hope this helps. If you still have questions, feel free to ask them!Carl Taylor
12/17/2025, 10:13 PMIt is not yet possible to use references (${...}) in module.yaml files or access the module configuration tree from plugin.yaml. We are planning on supporting this in some quality in the following releases.
Makes it impossible to do it currently.
All I'm looking for is some sort of way to generate resources/sources from one module into another, and without the classpath references from other modules, it looks hard to do.
Think of all the common api generators (typescript, openapi), database binding generators (jooq), security rule generators problem scenarios that many gradle plugins are created to solve.
Showing how to do something like this in the docs would rapidly increase the adoption for users who require these steps - I have since abandoned my PoC for amper, since gradle allows me to do this with ease due to the inputs/output spec of the gradle tasks.Fedor Ihnatkevich
12/18/2025, 1:48 PMIt is not yet possible to use references (${...}) in module.yaml
> Makes it impossible to do it currently.
You don't need references in module files to achieve what you need.
> how to take ONE module, and generate jvm-resources in ANOTHER
A plugin always contributes stuff in the module it is applied to. So you apply the plugin to the "target" module. And to reference the "source" module, you expose a sourceModule: Dependency.Local setting in the plugin settings, and pass the "source" module to it, like this:
# target/module.yaml
plugins:
my-codegen-plugin:
enabled: true
sourceModule: ../source-module
And then on the plugin side:
# my-codegen-plugin/plugin.yaml
tasks:
generate:
action: !myGenerateAction
# ...
compileClasspath:
dependencies: [ ${pluginSettings.sourceModule} ]
kind: compile
runtimeClasspath:
dependencies: [ ${pluginSettings.sourceModule} ]
kind: runtme
# markOutputsAs: ...
You don't need to write something like ${(../source-module).runtimeClasspath (which is not possible anyway now) you just use full form for the Classpath spec on the plugin side.
I'll see how to improve the docs (or the API) so that it's more clear how to implement cases like this.