All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.gradle.VaadinFlowPluginExtension.kt Maven / Gradle / Ivy

There is a newer version: 24.6.0
Show newest version
/**
 *    Copyright 2000-2023 Vaadin Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.vaadin.gradle

import com.vaadin.flow.server.Constants
import com.vaadin.flow.server.InitParameters
import com.vaadin.flow.server.frontend.FrontendTools
import com.vaadin.flow.server.frontend.installer.NodeInstaller
import com.vaadin.flow.server.frontend.installer.Platform
import groovy.lang.Closure
import groovy.lang.DelegatesTo
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import java.io.File
import javax.inject.Inject

public abstract class VaadinFlowPluginExtension @Inject constructor(private val project: Project) {
    /**
     * Whether or not we are running in productionMode. Defaults to false.
     * Responds to the `-Pvaadin.productionMode` property.
     */
    public abstract val productionMode: Property

    /**
     * The folder where webpack should output index.js and other generated
     * files. Defaults to `null` which will use the auto-detected value of
     * resoucesDir of the main SourceSet, usually `build/resources/main/META-INF/VAADIN/webapp/`.
     */
    public abstract val webpackOutputDirectory: Property

    /**
     * The folder where `package.json` file is located. Default is project root
     * dir.
     */
    public abstract val npmFolder: Property

    /**
     * A directory with project's frontend source files.
     *
     * Defaults to `frontend`
     */
    public abstract val frontendDirectory: Property

    /**
     * Whether to generate a bundle from the project frontend sources or not. Defaults to true.
     */
    public abstract val generateBundle: Property

    /**
     * Whether to run `npm install` after updating dependencies. Defaults to true.
     */
    public abstract val runNpmInstall: Property

    /**
     * Whether to generate embeddable web components from WebComponentExporter
     * inheritors. Defaults to true.
     */
    public abstract val generateEmbeddableWebComponents: Property

    /**
     * Defines the project frontend directory from where resources should be
     * copied from for use with webpack. Defaults to [Constants.LOCAL_FRONTEND_RESOURCES_PATH]
     */
    public abstract val frontendResourcesDirectory: Property

    /**
     * Whether to use byte code scanner strategy to discover frontend
     * components. Defaults to true.
     */
    public abstract val optimizeBundle: Property

    /**
     * Instructs to use pnpm for installing npm frontend resources. Default is [Constants.ENABLE_PNPM_DEFAULT]
     *
     * pnpm, a.k.a. performant npm, is a better front-end dependency management option.
     * With pnpm, packages are cached locally by default and linked (instead of
     * downloaded) for every project. This results in reduced disk space usage
     * and faster recurring builds when compared to npm.
     */
    public abstract val pnpmEnable: Property

    /**
     * Instructs to use bun for installing npm frontend resources. Default is false.
     *
     * bun, is a better front-end dependency management option.
     * With bun, packages are cached locally by default and linked (instead of
     * downloaded) for every project. This results in reduced disk space usage
     * and faster recurring builds when compared to npm.
     */
    public abstract val bunEnable: Property

    /**
     * Whether the globally installed pnpm tool is used. By default, the
     * pinned supported version of pnpm is used, see
     * [FrontendTools.DEFAULT_PNPM_VERSION].
     */
    public abstract val useGlobalPnpm: Property

    /**
     * Whether vaadin home node executable usage is forced. If it's set to
     * `true` then vaadin home 'node' is checked and installed if it's
     * absent. Then it will be used instead of globally 'node' or locally
     * installed installed 'node'.
     *
     * Defaults to false.
     */
    public abstract val requireHomeNodeExec: Property

    /**
     * Whether or not insert the initial Uidl object in the bootstrap index.html. Defaults to false.
     * Responds to the `-Pvaadin.eagerServerLoad` property.
     */
    public abstract val eagerServerLoad: Property

    /**
     * Application properties file in Spring project.
     * Defaults to `src/main/resources/application.properties`
     */
    public abstract val applicationProperties: Property

    /**
     * Default generated path of the OpenAPI json.
     *
     * Defaults to `generated-resources/openapi.json`.
     */
    public abstract val openApiJsonFile: Property

    /**
     * Java source folders for connect scanning.
     */
    public abstract val javaSourceFolder: Property

    /**
     * Java resource folder.
     */
    public abstract val javaResourceFolder: Property

    /**
     * The folder where flow will put TS API files for client projects.
     */
    public abstract val generatedTsFolder: Property

    /**
     * The node.js version to be used when node.js is installed automatically by
     * Vaadin, for example `"v16.0.0"`. Defaults to [FrontendTools.DEFAULT_NODE_VERSION].
     */
    public abstract val nodeVersion: Property

    /**
     * Download node.js from this URL. Handy in heavily firewalled corporate
     * environments where the node.js download can be provided from an intranet
     * mirror. Defaults to [NodeInstaller.DEFAULT_NODEJS_DOWNLOAD_ROOT].
     *
     * Example: `"https://nodejs.org/dist/"`.
     */
    public abstract val nodeDownloadRoot: Property

    /**
     * Allow automatic update of node installed to alternate location. Default `false`
     */
    public abstract val nodeAutoUpdate: Property

    /**
     * Defines the output directory for generated non-served resources, such as
     * the token file. Defaults to `build/vaadin-generated` folder.
     *
     * The plugin will automatically register
     * this as an additional resource folder, which should then be picked up by the IDE.
     * That will allow the app to run for example in Intellij with Tomcat.
     * Generating files into build/resources/main wouldn't work since Intellij+Tomcat
     * ignores that folder.
     *
     * The `flow-build-info.json` file is generated here.
     */
    public abstract val resourceOutputDirectory: Property

    /**
     * Defines the output folder used by the project.
     *
     * Default value is the `project.buildDir` and should not need to be changed.
     */
    public abstract val projectBuildDir: Property

    /**
     * Defines the npm packages to run postinstall for.
     */
    public abstract val postinstallPackages: ListProperty

    public val classpathFilter: ClasspathFilter = ClasspathFilter()

    /**
     * The name of the SourceSet to scan for Vaadin components - i.e. the classes that are annoated with
     * Vaadin annotations.
     *
     * Defaults to `"main"`
     */
    public abstract val sourceSetName: Property

    /**
     * The Gradle scope the Vaadin dependencies have been added to. Defaults to 'runtimeClasspath' if
     * no sourceSetName has been specified, or 'sourceSetNameRuntimeClasspath' if a non-main sourceset
     * has been set.
     */
    public abstract val dependencyScope: Property

    /**
     * The Gradle task that the `vaadinPrepareFrontend` task must run before. The target task should run before
     * or be the task that copies the files from the resources directories of the specified SourceSet to the relevant
     * output directory for that SourceSet. Defaults to 'processResources' if no sourceSetName has been specified, or
     * 'processSourceSetNameResources' if a non-main sourceset has been specified.
     */
    public abstract val processResourcesTaskName: Property

    /**
     * Parameter to control if frontend development server should be used in
     * development mode or not.
     *
     * Defaults to false.
     */
    public abstract val frontendHotdeploy: Property

    /**
     * Setting this to true will run {@code npm ci} instead of {@code npm install} when using npm.
     *
     * If using pnpm, the install will be run with {@code --frozen-lockfile} parameter.
     *
     * This makes sure that the versions in package lock file will not be overwritten and production builds are reproducible.
     */
    public abstract val ciBuild: Property

    /**
     * Enable skip of dev bundle rebuild if a dev bundle exists. Defaults to false.
     * @return `true` to skip dev bundle rebuild
     */
    public abstract val skipDevBundleBuild: Property

    /**
     * Setting this to `true` will force a build of the production build
     * even if there is a default production bundle that could be used.
     *
     * Created production bundle optimization is defined by
     * [optimizeBundle] parameter.
     *
     * Defaults to `false`.
     */
    public abstract val forceProductionBuild: Property

    /**
     * Prevents tracking state of the `vaadinPrepareFrontend` task, so that it
     * will re-run every time it is called.
     *
     * Setting this to `true` allows to always execute `vaadinPrepareFrontend`.
     *
     * Defaults to `false`, meaning that the task execution is skipped when its
     * outcomes are up-to-date, improving the overall build time.
     */
    public abstract val alwaysExecutePrepareFrontend: Property

    public fun filterClasspath(@DelegatesTo(value = ClasspathFilter::class, strategy = Closure.DELEGATE_FIRST) block: Closure<*>) {
        block.delegate = classpathFilter
        block.resolveStrategy = Closure.DELEGATE_FIRST
        block.call()
    }

    public fun filterClasspath(block: Action) {
        block.execute(classpathFilter)
    }

    public val effective: PluginEffectiveConfiguration get() = PluginEffectiveConfiguration.get(project)

    public companion object {
        public fun get(project: Project): VaadinFlowPluginExtension =
                project.extensions.getByType(VaadinFlowPluginExtension::class.java)
    }
}

public class PluginEffectiveConfiguration(
    private val project: Project,
    extension: VaadinFlowPluginExtension
) {
    public val productionMode: Provider = extension.productionMode
        .convention(false)
        .overrideWithSystemProperty("vaadin.productionMode")

    public val sourceSetName: Property = extension.sourceSetName
        .convention("main")

    public val webpackOutputDirectory: Provider = extension.webpackOutputDirectory
        .convention(sourceSetName.map { File(project.getBuildResourcesDir(it), Constants.VAADIN_WEBAPP_RESOURCES) })

    public val npmFolder: Provider = extension.npmFolder
        .convention(project.projectDir)

    public val frontendDirectory: Provider = extension.frontendDirectory
        .convention(File(project.projectDir, "frontend"))

    public val generateBundle: Provider = extension.generateBundle
        .convention(true)

    public val runNpmInstall: Provider = extension.runNpmInstall
        .convention(true)

    public val generateEmbeddableWebComponents: Provider = extension.generateEmbeddableWebComponents
        .convention(true)

    public val frontendResourcesDirectory: Property = extension.frontendResourcesDirectory
        .convention(File(project.projectDir, Constants.LOCAL_FRONTEND_RESOURCES_PATH))

    public val optimizeBundle: Property = extension.optimizeBundle
        .convention(true)

    public val pnpmEnable: Provider = extension.pnpmEnable
        .convention(Constants.ENABLE_PNPM_DEFAULT)
        .overrideWithSystemProperty(InitParameters.SERVLET_PARAMETER_ENABLE_PNPM)

    public val bunEnable: Provider = extension.bunEnable
        .convention(Constants.ENABLE_BUN_DEFAULT)
        .overrideWithSystemProperty(InitParameters.SERVLET_PARAMETER_ENABLE_BUN)

    public val useGlobalPnpm: Provider = extension.useGlobalPnpm
        .convention(Constants.GLOBAL_PNPM_DEFAULT)
        .overrideWithSystemProperty(InitParameters.SERVLET_PARAMETER_GLOBAL_PNPM)

    public val requireHomeNodeExec: Property = extension.requireHomeNodeExec
        .convention(false)

    public val eagerServerLoad: Provider = extension.eagerServerLoad
        .convention(false)
        .overrideWithSystemProperty("vaadin.eagerServerLoad")

    public val applicationProperties: Property = extension.applicationProperties
        .convention(File(project.projectDir, "src/main/resources/application.properties"))

    public val openApiJsonFile: Property = extension.openApiJsonFile
        .convention(project.layout.buildDirectory.file("generated-resources/openapi.json").asFile())

    public val javaSourceFolder: Property = extension.javaSourceFolder
        .convention(File(project.projectDir, "src/main/java"))

    public val javaResourceFolder: Property = extension.javaResourceFolder
        .convention(File(project.projectDir, "src/main/resources"))

    public val generatedTsFolder: Property = extension.generatedTsFolder
        .convention(File(project.projectDir, "frontend/generated"))

    public val nodeVersion: Property = extension.nodeVersion
        .convention(FrontendTools.DEFAULT_NODE_VERSION)

    public val nodeDownloadRoot: Property = extension.nodeDownloadRoot
        .convention(Platform.guess().nodeDownloadRoot)

    public val nodeAutoUpdate: Property = extension.nodeAutoUpdate
        .convention(false)

    public val resourceOutputDirectory: Property = extension.resourceOutputDirectory
        .convention(project.layout.buildDirectory.dir("vaadin-generated").asFile())

    public val projectBuildDir: Property = extension.projectBuildDir
        .convention(project.layout.buildDirectory.map { it.asFile.toString() })

    public val postinstallPackages: ListProperty = extension.postinstallPackages
        .convention(listOf())

    public val classpathFilter: ClasspathFilter = extension.classpathFilter

    public val dependencyScope: Property = extension.dependencyScope
        .convention(sourceSetName.map {
            if (it == "main") {
                "runtimeClasspath"
            } else {
                "${it}RuntimeClasspath"
            }
        })

    public val processResourcesTaskName: Property = extension.processResourcesTaskName
        .convention(sourceSetName.map {
            if (it == "main") {
                "processResources"
            } else {
                "process${it.replaceFirstChar(Char::titlecase)}Resources"
            }
        })

    public val frontendHotdeploy: Provider = extension.frontendHotdeploy
        .convention(false)
        .overrideWithSystemProperty(InitParameters.FRONTEND_HOTDEPLOY)

    public val ciBuild: Provider = extension.ciBuild
        .convention(false)
        .overrideWithSystemProperty(InitParameters.CI_BUILD)

    public val skipDevBundleBuild: Property = extension.skipDevBundleBuild
        .convention(false)

    public val forceProductionBuild: Provider = extension.forceProductionBuild
        .convention(false)
        .overrideWithSystemProperty(InitParameters.FORCE_PRODUCTION_BUILD)

    public val alwaysExecutePrepareFrontend: Property = extension.alwaysExecutePrepareFrontend
        .convention(false)

    /**
     * Finds the value of a boolean property. It searches in gradle and system properties.
     *
     * If the property is defined in both gradle and system properties, then the gradle property is taken.
     *
     * @param propertyName the property name
     * @return a new provider of the value, which either takes the original value if the system/gradle property is not present,
     * `true` if it's defined or if it's set to "true" and `false` otherwise.
     */
    private fun Provider.overrideWithSystemProperty(propertyName: String) : Provider = map { originalValue ->
        project.getBooleanProperty(propertyName) ?: originalValue
    }

    override fun toString(): String = "PluginEffectiveConfiguration(" +
            "productionMode=${productionMode.get()}, " +
            "webpackOutputDirectory=${webpackOutputDirectory.get()}, " +
            "npmFolder=${npmFolder.get()}, " +
            "frontendDirectory=${frontendDirectory.get()}, " +
            "generateBundle=${generateBundle.get()}, " +
            "runNpmInstall=${runNpmInstall.get()}, " +
            "generateEmbeddableWebComponents=${generateEmbeddableWebComponents.get()}, " +
            "frontendResourcesDirectory=${frontendResourcesDirectory.get()}, " +
            "optimizeBundle=${optimizeBundle.get()}, " +
            "pnpmEnable=${pnpmEnable.get()}, " +
            "bunEnable=${bunEnable.get()}, " +
            "ciBuild=${ciBuild.get()}, " +
            "forceProductionBuild=${forceProductionBuild.get()}, " +
            "useGlobalPnpm=${useGlobalPnpm.get()}, " +
            "requireHomeNodeExec=${requireHomeNodeExec.get()}, " +
            "eagerServerLoad=${eagerServerLoad.get()}, " +
            "applicationProperties=${applicationProperties.get()}, " +
            "openApiJsonFile=${openApiJsonFile.get()}, " +
            "javaSourceFolder=${javaSourceFolder.get()}, " +
            "javaResourceFolder=${javaResourceFolder.get()}, " +
            "generatedTsFolder=${generatedTsFolder.get()}, " +
            "nodeVersion=${nodeVersion.get()}, " +
            "nodeDownloadRoot=${nodeDownloadRoot.get()}, " +
            "nodeAutoUpdate=${nodeAutoUpdate.get()}, " +
            "resourceOutputDirectory=${resourceOutputDirectory.get()}, " +
            "projectBuildDir=${projectBuildDir.get()}, " +
            "postinstallPackages=${postinstallPackages.get()}, " +
            "sourceSetName=${sourceSetName.get()}, " +
            "dependencyScope=${dependencyScope.get()}, " +
            "processResourcesTaskName=${processResourcesTaskName.get()}, " +
            "skipDevBundleBuild=${skipDevBundleBuild.get()}, " +
            "alwaysExecutePrepareFrontend=${alwaysExecutePrepareFrontend.get()}, " +
            "frontendHotdeploy=${frontendHotdeploy.get()}" +
            ")"
    public companion object {
        public fun get(project: Project): PluginEffectiveConfiguration =
            PluginEffectiveConfiguration(project, VaadinFlowPluginExtension.get(project))
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy