Sharing variables and methods between convention plugins

For the groovy dialect, I’m trying to simplify the use of version catalogs in convention plugins. Because of a resolution order behavior (AFAIK) the versions can’t be specified directly and we need to revert to string-y declarations. That is, given a convention plugin com.my.base.gradle:

plugins {
    id("java")
}

dependencies {
    implementation(libs.my.lib)
}

does not work (and I tested). A workaround is to do the following:

plugins {
    id("java")
}

def libs = project.extensions.getByType(VersionCatalogsExtension).named("libs")

dependencies {
    implementation(libs.findLibrary("my.lib").get())
}

Because this pattern is used in several places, I created a method inside the plugin that shortens it:

//...

dependencies {
    implementation(libOf(libs, "my.lib")
}

def libOf(libs, name) {
	libs.findLibrary(name).get()
}

and similarly for versions and bundles.
However, I need to do this for every plugin. A dep plugin:

plugins {
    id("com.my.base")
}

does not “inherit” the definitions in the applied plugin.

If I’d like to share the def libs = ... and the convenience methods amongst the plugins, what is the way to do that? Also, if there’s another way of achieving the same goal of using catalogs in convention plugins I’d like to know.

If I’d like to share the def libs = ... and the convenience methods amongst the plugins, what is the way to do that?

Imagine (that’s oversimplified what is happening) the contents of the script as the body of the apply method of a plugin class with the methods and variables being methods and properties of that class, so yes, you cannot simply access them from other plugins, but just declare them in normal classes, not script files, then you can use them if you really think you need them, which you do not.

Also, if there’s another way of achieving the same goal of using catalogs in convention plugins I’d like to know.

Sure, your first snippet is perfectly fine for Groovy DSL precompiled script plugins usually.
Maybe you should share more details on why you think it does not work, optimally as MCVE.