How to add gradle task to compile additional SourceSet - javascript

I have a KotlinJS-project using
plugins {
kotlin("js")
}
Kotlin-compiler-plugin that generates new .kt-files (right now in folder $buildDir/src/main/generated) when task build is executed.
Unfortunately I have to call build a second time to compile the generated sources
Therefore I would like to add a task that compiles the generated sources by
tasks.register("compileGenerated", org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile::class) {
sourceSets {
create("generated") {
kotlin.srcDir("${buildDir}/src/generated/kotlin/")
dependsOn(sourceSets.getByName("main"))
}
}
}
That task I would call at the end of each build using finalizedBy
Trying it I just get the following message:
> Could not create task ':examples:nestedmodel:model:compileGenerated'.
> Project#afterEvaluate(Action) on project ':example' cannot be executed in the current context.
To be honest I have no idea, why...

Related

kotlin-js gradle outputfile wrong

I'm working on kotlin-js example. I used this sample. When I was build frontend module(as shown in the picture below) I can't see web folder. But the resource files should be in web folder. What's wrong?
Project Source Image
buildscript {
ext.kotlin_version = '1.2.30'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
group 'example'
version '1.0-SNAPSHOT'
apply plugin: 'kotlin2js'
repositories {
mavenCentral()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-test-js:$kotlin_version"
}
build.doLast {
configurations.compile.each { File file ->
copy {
includeEmptyDirs = false
from zipTree(file.absolutePath)
into "${projectDir}/web"
include { fileTreeElement ->
def path = fileTreeElement.path
path.endsWith(".js") && (path.startsWith("META-INF/resources/") || !path.startsWith("META-INF/"))
}
}
}
}
compileKotlin2Js {
kotlinOptions.outputFile = "${projectDir}/web/output.js"
kotlinOptions.sourceMap = true
}
Your copy { ... } block only sets up copying of the dependency files from the compile configuration, and it does not copy the resources of your project into web. Neither does the compileKotlin2Js task, which only puts the compiled Kotlin classes into that directory.
To copy the resources of the main source set, you can add another copy { ... } block, like this:
build.doLast {
// ...
copy {
from sourceSets.main.output.resourcesDir
into "${projectDir}/web"
}
}
And note that if you only copy the files, you may end up with stale output files left from the previous run (if a file no more exists in the source directory, its copy is not deleted from the target). Instead, consider using the default output file location for the compileKotlin2Js task and a Sync task to synchronize the directories, as described in this guide (it does not mention the resources; add them with from ... as suggested above). If you need to customize the output file name, you can use archivesBaseName for that.

Using webpack to build a dependency before compiling the build

I'm using inline styles & my theme comes from a file called themes.json, which I create & inject into my build folder before calling webpack. I'd like webpack to handle this, too.
My first attempt was to use a plugin:
compiler.plugin('compilation', async(compilation, callback) => {
const themes = {}; // empty for this example
compilation.assets['themes.json'] = {
source: function() {
return themes;
},
size: function() {
return themes.length;
}
};
callback();
}
However, since plugins run async or parallel, I come across a race condition where my index.js that has a const themes = require('../build/themes.json') is trying to require something before it exists.
How can I make sure themes.json exists and is useable within my build? I see 3 possibilities:
There's a way to run plugins in serial that I don't know about
I somehow use a loader to perform this
I write a plugin that looks for require('../build/themes.json') and when it finds it, it creates & injects the themes.
Any help on the right way to do this?

Running a gradle task multiple times with different parameters

I am trying to write a gradle task which will minify all my project's javascript files. I am using a gradle library: com.eriwen.gradle.js. This library contains a task called minifyJs where we define the source file we want to minify and the destination of the minified file:
minifyJs {
source = file(sourcePathString)
dest = file(targetPathString)
}
What I want to do is call execute this task for EVERY javascript file in my project and produce a minified version of it in a new path for EACH file. This would require me to run the minifyJs task multiple times each time with different source and dest values, but I can't seem to find a solution on how to do this. One person had suggested that we use a loop to create a new task of type: minifyJs for each javascript file but this takes a huge amount of time and will create 250+ tasks i.e. not effective at all.
Since calling a task inside another task doesn't work (and using task.execute() is bad practice) I'm essentially looking for a workaround that lets me achieve this:
task customMinify {
def jsFileTree = fileTree('my/javascript/files')
jsFileTree.forEach {
def jsFile = it
minifyJs {
source = file(jsFile.getPath())
dest = file('new/path/to/file.js')
}
}
}
which obviously doesn't work since we can't call minifyJs inside another task.
I'm really sorry that this gap has continued to exist in the gradle-js-plugin.
Since generating tasks won't do, I suggest that you write a custom task under buildSrc combining my JsMinifier and the MinifyJsTask.
If you're willing to wait 8 hours or so, I can write an implementation of this later if you like.
EDIT: Here's a gist for a ClosureMinifyTask you can throw in buildSrc/src/main/groovy/com/eriwen/gradle/js/tasks and it'll minify each file individually and produce individual source map files etc.
buildSrc/build.gradle:
repositories {
mavenCentral()
}
dependencies {
compile localGroovy()
compile gradleApi()
compile ('com.google.javascript:closure-compiler:v20151015') {
exclude module: 'junit'
}
}
Sample Usage:
task mini(type: com.foo.bar.ClosureMinifyTask) {
source = "src/js"
dest = "${buildDir}/js/minified"
}

Grunt and hood.ie test database

I'm currently running my test suite on AngularJS using Grunt, Karma, Jasmine and Protractor. The database library I'm using is hood.ie, which is a library on top of CouchDB. I start hood.ie using the following code in my Gruntfile:
hoodie: {
start: {
options: {
callback: function(config) {
grunt.config.set('connect.proxies.0.port', config.stack.couch.port);
}
}
}
},
However, I would like to have a separate database for running tests, which automatically resets afterwards. This way, the production data won't conflict with the tests.
How should I approach this? I would assume there's some kind of standard way of doing this, as I can imagine other people have come across the same problem, but I'm unable to find anything on the internet.
Currently, this seems to be impossible as the hoodie server does not support it. The best way to go about this is to modify it yourself at Hood.ie server Github repository by adding a parameter to define the folder in which the data will be stored, which is at the moment hardcoded to 'data' (https://github.com/hoodiehq/hoodie-server/blob/master/lib/core/environment.js#L48)
Something similar to this should work:
app_path: path.resolve(project_dir, argv.folder || 'data')
As the hoodie task is a 'multitask' you could have a test target in your hood.ie grunt task specific to testing, and then reference this in a grunt command used to run tests e.g:
hoodie: {
start: {
options: {
callback: function(config) {
grunt.config.set('connect.proxies.0.port', config.stack.couch.port);
}
}
},
test: {
options: {
callback: function(config) {
// Make test specific changes here.
}
}
}
}
// The task that runs tests first starting test deps. 'runtests' can be anything you want.
grunt.registerTask('test', 'Run unit tests', ['hoodie:test', 'runtests']);
Note: this will mean any other times you're referencing the hoodie task you'll need to be explicit as otherwise all the specified targets will be run. See this documentation on multitasks for more info. In this example you'd change hoodie to hoodie:start to run the 'start' task as previously defined.

Define a Gradle task with inputs from project dependencies?

Background: I have a multi-project Gradle build, and I've defined a Gradle task which runs JavaScript unit tests in an Exec task. The inputs to this task are the JavaScript files in the project, so it's only re-run if one of the source files are modified. The task is added to all JavaScript projects from a master project.
Question: I want to extend this so that the tests are re-run if JavaScript files in the project, or in any of its project dependencies are changed. How is this best done?
The code below works if placed in each subproject build file (after the dependency declaration), but we have 20+ JavaScript subprojects and I'd like to stay DRY.
project.ext.jsSourceFiles = fileTree("src/").include("**/*.js*")
task testJavaScript(type: Exec, dependsOn: configurations.js) {
inputs.files resolveJavascriptDependenciesFor(project)
outputs.file "report.xml"
// Run tests in JSTestDriver using command line call...
}
def resolveJavascriptDependenciesFor(project) {
def files = project.jsSourceFiles
project.configurations.js.allDependencies.each {
files = files + resolveJavascriptDependenciesFor(it.dependencyProject)
}
return files
}
Is there a better solution? Maybe where I don't have to resolve all file dependencies myself?
As written in the answer before, adding the jsTest task within a subprojects closure would make it very easy to add jstesting support for every subproject. I think you can ease your inputs setup by declaring source files as dependencies:
dependencies {
js filetree("src/main").include("**/*.js")
}
and
subprojects { subproj ->
task testJavaScript(type: Exec, dependsOn: configurations.js) {
inputs.files subproj.configurations.js
outputs.file "report.xml"
commandLine ...
}
}
Would it be possible to do something like this?
allprojects {project ->
task testJavaScript(type: Exec, dependsOn: configurations.js) {
inputs.files resolveJavascriptDependenciesFor(project)
// Run tests in JSTestDriver using command line call...
}
}
def resolveJavascriptDependenciesFor(project) {
def files = project.jsSourceFiles
project.configurations.js.allDependencies.each {
files = files + resolveJavascriptDependenciesFor(it.dependencyProject)
}
return files
}
Tha way the task is on all projects, and wil be called recursively.
Not completely sure this works, but I think its the way to go
I've found a solution that works but isn't great, using the same dependency specification as in the question. It's to load the gradle files in a slightly different order in the master project.
Master build.gradle:
subprojects {
configurations {
js
}
// Apply the subproject's build.gradle, but with another name (that isn't automatically loaded)
if (project.file('depends.gradle').exists()) {
apply from: project.file('depends.gradle')
}
apply from: project.parent.file('javaScriptProjectTasks.gradle')
}
Compare to the previous, non working master build.gradle
subprojects {
configurations {
js
}
apply from: project.parent.file('javaScriptProjectTasks.gradle')
// The subproject's build.gradle is automatically loaded
}

Categories