2021-08-09 00:27:16 +02:00
/ *
* Copyright 2020 dorkbox , llc
*
* 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 dorkbox.gradleVaadin
2021-08-10 07:27:25 +02:00
import dorkbox.gradleVaadin.node.deps.DependencyScanner
2021-08-09 00:27:16 +02:00
import dorkbox.gradleVaadin.node.npm.proxy.ProxySettings
import dorkbox.gradleVaadin.node.npm.task.NpmInstallTask
import dorkbox.gradleVaadin.node.npm.task.NpmSetupTask
import dorkbox.gradleVaadin.node.npm.task.NpmTask
import dorkbox.gradleVaadin.node.npm.task.NpxTask
import dorkbox.gradleVaadin.node.task.NodeSetupTask
import dorkbox.gradleVaadin.node.task.NodeTask
2021-10-07 16:07:21 +02:00
import dorkbox.vaadin.VaadinApplication
2021-09-11 21:47:11 +02:00
import org.gradle.api.Action
2021-08-09 00:27:16 +02:00
import org.gradle.api.Plugin
import org.gradle.api.Project
2021-09-08 00:38:02 +02:00
import org.gradle.api.Task
2021-09-11 21:47:11 +02:00
import org.gradle.api.execution.TaskExecutionGraph
2021-09-08 00:38:02 +02:00
import org.gradle.api.tasks.TaskInputs
2021-09-23 16:56:27 +02:00
import org.gradle.jvm.tasks.Jar
2021-08-09 00:27:16 +02:00
/ * *
* For managing Vaadin gradle tasks
2021-09-27 02:42:46 +02:00
*
* NOTE : Vaadin css resources are compiled into the stats . json file , they are NOT loaded " statically " from the webserver
2021-08-09 00:27:16 +02:00
* /
2021-10-07 16:07:21 +02:00
@Suppress ( " UnstableApiUsage " , " unused " , " SameParameterValue " , " ObjectLiteralToLambda " )
2021-08-09 00:27:16 +02:00
class Vaadin : Plugin < Project > {
2021-08-29 06:15:01 +02:00
companion object {
2021-10-07 16:07:21 +02:00
internal const val NODE _GROUP = " Node "
internal const val NPM _GROUP = " npm "
2021-08-29 06:15:01 +02:00
2021-10-07 16:07:21 +02:00
internal const val SHUTDOWN _TASK = " shutdownCompiler "
internal const val compileDevName = " vaadinDevelopment "
internal const val compileProdName = " vaadinProduction "
2021-08-29 06:15:01 +02:00
/ * *
* Recursively resolves all child dependencies of the project
*
* THIS MUST BE IN " afterEvaluate " or run from a specific task .
* /
fun resolveAllDependencies ( project : Project ) : List < DependencyScanner . DependencyData > {
// NOTE: we cannot createTree("compile") and createTree("runtime") using the same exitingNames and expect correct results.
// This is because a dependency might exist for compile and runtime, but have different children, therefore, the list
// will be incomplete
// there will be DUPLICATES! (we don't care about children or hierarchy, so we remove the dupes)
return ( DependencyScanner . scan ( project , " compileClasspath " ) +
DependencyScanner . scan ( project , " runtimeClasspath " )
) . toSet ( ) . toList ( )
}
/ * *
* Recursively resolves all child compile dependencies of the project
*
* THIS MUST BE IN " afterEvaluate " or run from a specific task .
* /
fun resolveRuntimeDependencies ( project : Project ) : DependencyScanner . ProjectDependencies {
val projectDependencies = mutableListOf < DependencyScanner . Dependency > ( )
val existingNames = mutableMapOf < String , DependencyScanner . Dependency > ( )
DependencyScanner . createTree ( project , " runtimeClasspath " , projectDependencies , existingNames )
return DependencyScanner . ProjectDependencies ( projectDependencies , existingNames . map { it . value } )
}
}
2021-08-09 00:27:16 +02:00
private lateinit var project : Project
private lateinit var config : VaadinConfig
2021-10-07 16:07:21 +02:00
/** Useful, so we can announce the version of vaadin we are using */
val version = VaadinApplication . vaadinVersion
2021-09-09 01:07:27 +02:00
2021-09-08 00:38:02 +02:00
private fun newTask ( dependencyTask : Task ,
taskName : String ,
description : String ,
inputs : TaskInputs . ( ) -> Unit = { } ,
action : Task . ( vaadinCompiler : VaadinCompiler ) -> Unit ) : Task {
2021-09-24 03:34:24 +02:00
return newTask ( dependencyTask . name , taskName , description , inputs , action )
}
@Suppress ( " ObjectLiteralToLambda " )
2021-10-07 16:07:21 +02:00
private fun newTask ( dependencyName : String ,
2021-09-24 03:34:24 +02:00
taskName : String ,
description : String ,
inputs : TaskInputs . ( ) -> Unit = { } ,
action : Task . ( vaadinCompiler : VaadinCompiler ) -> Unit ) : Task {
2021-10-07 16:07:21 +02:00
return newTask ( listOf ( dependencyName ) , taskName , description , inputs , action )
}
private fun newTask ( dependencyName : List < String > ,
taskName : String ,
description : String ,
inputs : TaskInputs . ( ) -> Unit = { } ,
action : Task . ( vaadinCompiler : VaadinCompiler ) -> Unit ) : Task {
2021-09-08 00:38:02 +02:00
return project . tasks . create ( taskName ) . apply {
2021-10-07 16:07:21 +02:00
dependsOn ( dependencyName . toTypedArray ( ) )
finalizedBy ( SHUTDOWN _TASK )
2021-09-08 00:38:02 +02:00
group = " vaadin "
this . description = description
inputs ( this . inputs )
2021-09-20 03:24:09 +02:00
doLast ( object : Action < Task > {
override fun execute ( task : Task ) {
action ( task , VaadinConfig [ project ] . vaadinCompiler )
}
} )
2021-09-08 00:38:02 +02:00
}
}
2021-10-07 16:07:21 +02:00
2021-08-09 00:27:16 +02:00
override fun apply ( project : Project ) {
this . project = project
// https://discuss.gradle.org/t/can-a-plugin-itself-add-buildscript-dependencies-and-then-apply-a-plugin/25039/4
apply ( " java " )
// Create the Plugin extension object (for users to configure publishing).
2021-08-31 05:27:28 +02:00
config = VaadinConfig . create ( project )
2021-08-09 00:27:16 +02:00
project . repositories . apply {
2021-09-09 23:52:32 +02:00
maven { it . setUrl ( " https://maven.vaadin.com/vaadin-addons " ) } // Vaadin Addons
maven { it . setUrl ( " https://maven.vaadin.com/vaadin-prereleases " ) } // Pre-releases
maven { it . setUrl ( " https://oss.sonatype.org/content/repositories/vaadin-snapshots " ) } // Vaadin Snapshots
2021-08-09 00:27:16 +02:00
}
project . dependencies . apply {
2021-08-31 06:35:36 +02:00
add ( " implementation " , " com.vaadin:vaadin: ${VaadinConfig.VAADIN_VERSION} " )
2021-09-20 04:05:21 +02:00
add ( " implementation " , " com.dorkbox:VaadinUndertow: ${VaadinConfig.MAVEN_VAADIN_GRADLE_VERSION} " )
2021-12-09 02:02:02 +01:00
add ( " implementation " , " io.undertow:undertow-core: ${VaadinConfig.UNDERTOW_VERSION} " )
add ( " implementation " , " io.undertow:undertow-servlet: ${VaadinConfig.UNDERTOW_VERSION} " )
add ( " implementation " , " io.undertow:undertow-websockets-jsr: ${VaadinConfig.UNDERTOW_VERSION} " )
2021-08-09 00:27:16 +02:00
}
2021-08-29 06:15:01 +02:00
// NOTE: NPM will ALWAYS install packages to the "node_modules" directory that is a sibling to the packages.json directory!
2021-08-09 00:27:16 +02:00
addGlobalTypes ( )
addTasks ( )
addNpmRule ( )
2021-09-11 21:47:11 +02:00
project . gradle . taskGraph . whenReady ( object : Action < TaskExecutionGraph > {
override fun execute ( graph : TaskExecutionGraph ) {
2021-08-29 06:15:01 +02:00
// every other task will do nothing (run as dev mode).
2021-09-23 14:55:35 +02:00
val allTasks = graph . allTasks
var hasVaadinTask = false
if ( allTasks . firstOrNull { it . name == compileProdName } != null ) {
2021-09-11 21:47:11 +02:00
config . productionMode . set ( true )
2021-09-23 14:55:35 +02:00
hasVaadinTask = true
}
if ( allTasks . firstOrNull { it . name == compileDevName } != null ) {
hasVaadinTask = true
}
if ( hasVaadinTask ) {
val jarTasks = allTasks . filter { it . name . endsWith ( " jar " ) }
2021-09-23 16:56:27 +02:00
2021-09-23 14:55:35 +02:00
if ( jarTasks . isNotEmpty ( ) ) {
2021-09-23 16:56:27 +02:00
project . tasks . withType ( Jar :: class . java ) {
// we ALWAYS want to make sure that this task runs. If *something* is cached, then there jar file output will be incomplete.
2021-09-24 03:34:24 +02:00
it . outputs . cacheIf { false }
2021-09-23 16:56:27 +02:00
it . outputs . upToDateWhen { false }
}
2021-09-23 14:55:35 +02:00
}
2021-09-11 21:47:11 +02:00
}
2021-08-29 06:15:01 +02:00
}
2021-09-11 21:47:11 +02:00
} )
2021-08-29 06:15:01 +02:00
2021-10-07 16:07:21 +02:00
project . tasks . create ( SHUTDOWN _TASK ) . apply {
2021-09-11 21:47:11 +02:00
doLast ( object : Action < Task > {
override fun execute ( t : Task ) {
val vaadinCompiler = VaadinConfig [ project ] . vaadinCompiler
// every task MUST call shutdown in order to close the class-scanner (otherwise it will hold open files)
vaadinCompiler . finish ( )
}
} )
2021-08-30 16:11:15 +02:00
}
2021-09-24 03:34:24 +02:00
// NOTE! our class-scanner scans COMPILED CLASSES, so it is required to depend (at some point) on class compilation!
2021-10-07 16:07:21 +02:00
val generateWebComponents = newTask ( listOf ( NodeSetupTask . NAME , " classes " ) , " generateWebComponents " , " Generate Vaadin web components " )
2021-09-08 00:38:02 +02:00
{ vaadinCompiler ->
VaadinConfig [ project ] . vaadinCompiler . log ( )
vaadinCompiler . generateWebComponents ( )
}
2021-08-30 16:11:15 +02:00
2021-09-08 00:38:02 +02:00
val createMissingPackageJson = newTask ( generateWebComponents , " createMissingPackageJson " , " Prepare Vaadin frontend " )
{ vaadinCompiler ->
2021-09-20 03:24:09 +02:00
// VaadinCompile.print()
2021-09-08 00:38:02 +02:00
vaadinCompiler . createMissingPackageJson ( )
2021-08-29 06:15:01 +02:00
}
2021-08-09 00:27:16 +02:00
2021-09-08 00:38:02 +02:00
val prepareJsonFiles = newTask ( createMissingPackageJson , " prepareJsonFiles " , " Prepare Vaadin frontend " ,
{
// files(vaadinCompiler.jsonPackageFile)
} )
{ vaadinCompiler ->
vaadinCompiler . prepareJsonFiles ( )
}
2021-08-09 00:27:16 +02:00
2021-09-08 00:38:02 +02:00
val copyJarResources = newTask ( prepareJsonFiles , " copyJarResources " , " Compile Vaadin resources for Production " )
{ vaadinCompiler ->
vaadinCompiler . copyJarResources ( )
2021-08-29 06:15:01 +02:00
}
2021-09-08 00:38:02 +02:00
val copyLocalResources = newTask ( copyJarResources , " copyLocalResources " , " Compile Vaadin resources for Production " )
{ vaadinCompiler ->
vaadinCompiler . copyFrontendResourcesDirectory ( )
}
2021-08-29 06:15:01 +02:00
2021-09-08 00:38:02 +02:00
val createTokenFile = newTask ( copyLocalResources , " createTokenFile " , " Create Vaadin token file " )
{ vaadinCompiler ->
vaadinCompiler . createTokenFile ( )
2021-08-29 06:15:01 +02:00
}
2021-09-08 00:38:02 +02:00
val updateWebPack = newTask ( createTokenFile , " updateWebPack " , " Compile Vaadin resources for Production " )
{ vaadinCompiler ->
vaadinCompiler . fixWebpackTemplate ( )
}
2021-08-29 06:15:01 +02:00
2021-09-08 00:38:02 +02:00
// last one from NodeTasks.java
val enableImportsUpdate = newTask ( updateWebPack , " enableImportsUpdate " , " Compile Vaadin resources for Production " )
{ vaadinCompiler ->
vaadinCompiler . enableImportsUpdate ( )
}
2021-08-29 06:15:01 +02:00
2021-09-08 00:38:02 +02:00
// FOR DEV MODE, THIS IS AS FAR AS WE GO
// last DevModeHandler start
2021-08-29 06:15:01 +02:00
2021-09-08 00:38:02 +02:00
// the plugin on start needs to rewrite DevModeInitializer.initDevModeHandler so that all these tasks aren't run every time for dev mode
// because that is really slow to start up??
// ALTERNATIVELY, devmodeinit runs the same thing as the gradle plugin, but the difference is we only run the full gradle version
// for a production build (webpack is compiled instead of running dev-mode, is the only difference...).
// the only problem, is when running as a JPMS module...
// to solve this, we could modify the byte-code -- then RECREATE the jar (which we would then startup)
// OR.. we modify the bytecode ON COMPILE, so that a "run" would always include the modified jar
// or we just include our own version
val generateWebPack = newTask ( enableImportsUpdate , " generateWebPack " , " Compile Vaadin resources for Production " )
{ vaadinCompiler ->
vaadinCompiler . generateWebPack ( )
2021-08-29 06:15:01 +02:00
}
project . tasks . create ( compileDevName ) . apply {
2021-09-24 03:34:24 +02:00
dependsOn ( createTokenFile )
2021-10-07 16:07:21 +02:00
finalizedBy ( SHUTDOWN _TASK )
2021-09-08 00:38:02 +02:00
2021-08-09 00:27:16 +02:00
group = " vaadin "
description = " Compile Vaadin resources for Development "
2021-09-24 03:34:24 +02:00
outputs . cacheIf { false }
outputs . upToDateWhen { false }
2021-08-09 00:27:16 +02:00
2021-10-07 16:07:21 +02:00
inputs . files (
" ${project.projectDir} /package.json " ,
" ${project.projectDir} /package-lock.json " ,
" ${project.projectDir} /webpack.config.js " ,
" ${project.projectDir} /webpack.production.js "
)
outputs . dir ( " ${project.buildDir} /config " )
outputs . dir ( " ${project.buildDir} /resources " )
outputs . dir ( " ${project.buildDir} /nodejs " )
outputs . dir ( " ${project.buildDir} /node_modules " )
2021-08-09 00:27:16 +02:00
}
2021-09-08 00:38:02 +02:00
2021-08-29 06:15:01 +02:00
project . tasks . create ( compileProdName ) . apply {
2021-09-24 03:34:24 +02:00
dependsOn ( generateWebPack )
2021-10-07 16:07:21 +02:00
finalizedBy ( SHUTDOWN _TASK )
2021-08-09 00:27:16 +02:00
group = " vaadin "
description = " Compile Vaadin resources for Production "
2021-09-24 03:34:24 +02:00
outputs . cacheIf { false }
outputs . upToDateWhen { false }
2021-10-07 16:07:21 +02:00
2021-09-24 03:34:24 +02:00
// inputs.files(
// "${project.projectDir}/package.json",
// "${project.projectDir}/package-lock.json",
// "${project.projectDir}/webpack.config.js",
// "${project.projectDir}/webpack.production.js"
// )
//
// outputs.dir("${project.buildDir}/resources/main/META-INF/resources/VAADIN")
// outputs.dir("${project.buildDir}/node_modules")
2021-08-09 00:27:16 +02:00
}
2021-10-07 16:07:21 +02:00
project . tasks . withType ( Jar :: class . java ) {
// we ALWAYS want to make sure that this task runs when jar files are created (since we consider a jar to be the final production package for this project
it . dependsOn ( compileProdName )
}
2021-09-24 03:34:24 +02:00
// config.addSubprojects()
// project.childProjects.values.forEach {
// it.pluginManager.apply(Vaadin::class.java)
// }
2021-08-09 00:27:16 +02:00
}
// required to make sure the plugins are correctly applied. ONLY applying it to the project WILL NOT work.
// The plugin must also be applied to the root project
private fun apply ( id : String ) {
if ( project . rootProject . pluginManager . findPlugin ( id ) == null ) {
project . rootProject . pluginManager . apply ( id )
}
if ( project . pluginManager . findPlugin ( id ) == null ) {
project . pluginManager . apply ( id )
}
}
private fun addGlobalTypes ( ) {
addGlobalType < NodeTask > ( )
addGlobalType < NpmTask > ( )
addGlobalType < NpxTask > ( )
addGlobalType < ProxySettings > ( )
}
private inline fun < reified T > addGlobalType ( ) {
project . extensions . extraProperties [ T :: class . java . simpleName ] = T :: class . java
}
private fun addTasks ( ) {
2021-09-09 01:07:27 +02:00
project . tasks . register ( NpmInstallTask . NAME , NpmInstallTask :: class . java )
project . tasks . register ( NodeSetupTask . NAME , NodeSetupTask :: class . java )
project . tasks . register ( NpmSetupTask . NAME , NpmSetupTask :: class . java )
2021-08-09 00:27:16 +02:00
}
private fun addNpmRule ( ) { // note this rule also makes it possible to specify e.g. "dependsOn npm_install"
2021-09-09 23:52:32 +02:00
project . tasks . addRule ( " Pattern: \" npm_<command> \" : Executes an NPM command. " ) { taskName ->
2021-08-09 00:27:16 +02:00
if ( taskName . startsWith ( " npm_ " ) ) {
project . tasks . create ( taskName , NpmTask :: class . java ) {
val tokens = taskName . split ( " _ " ) . drop ( 1 ) // all except first
2021-09-09 23:52:32 +02:00
it . npmCommand . set ( tokens )
2021-08-09 00:27:16 +02:00
if ( tokens . first ( ) . equals ( " run " , ignoreCase = true ) ) {
2021-09-09 23:52:32 +02:00
it . dependsOn ( NpmInstallTask . NAME )
2021-08-09 00:27:16 +02:00
}
}
}
}
}
}