CoffeeScript and sbt-concat - scala

I'm having trouble concatenating and fingerprinting all the CoffeeScript files in Play application. Everything works fine for JavaScript files with build.sbt like this one
pipelineStages := Seq(concat, digest)
Concat.groups := Seq(
"javascripts/app.js" -> group(((sourceDirectory in Assets).value / "javascripts") * "*.js")
)
But when sourceDirectory is changed to resourcesManaged that supposedly contains compiled CoffeeScript files sbt-concat doesn't pick them up.

sbt-coffeescript, and all other official source task plugins, don't put their files in resourcesManaged in Assets, but instead their own sub-directory in target/web/<taskname>. They scope the resourcesManaged setting to their main task, in this case this means resourcesManaged in (Assets, coffeescript) and resourcesManaged in (TestAssets, coffeescript).
When you run sbt coffeescript you can see the files are output to target/web/coffeescript/main. You can verify this by running show web-assets:coffeescript::resourceManaged from the sbt console.

Related

sbt 0.13.16 - How do I customize triggered execution (file watch)

Right now, most of the changes that I am making are to config files and build.sbt files. That being said, I still want to trigger execution (ex. compile, test, reload, etc) when I touch these files. How do I customize sbt triggered execution to execute when any file in the project is modified?
https://www.scala-sbt.org/0.13/docs/Howto-Triggered.html
https://www.scala-sbt.org/0.13/docs/Howto-Triggered.html
https://www.scala-sbt.org/1.0/docs/Howto-Triggered.html
watchSources += baseDirectory.value
I just do this to watch the entire base directory:
watchSources := Seq(file("."))

Scala.js compilation destination

I'm working on a Scala.js cross project where the jvm folder represents my server application and jsrepresents my scala.js code.
Whenever i compile my scala.js code via sbt crossJS/fastOptJS the compiled JS ends up in ./js/target/scala-2.11/web-fastopt.js.
I need to have this compiled JS file accessible in the resources of the server project in the jvm folder, so i can server it through my web application. I think i have to do something with artifactPath but i can't seem to get any results from my experiments thus far.
You can simply set the artifactPath of the fastOptJS task (or the fullOptJS task) to the (managed) resources directory of your JVM project:
// In the JS project's settings
artifactPath in fastOptJS in Compile :=
(resourceManaged in jvm in Compile).value /
((moduleName in fastOptJS).value + "-fastopt.js"))
This will put it in the directory, if the you run the fastOptJS task. However, it will not be included in sbt's resources task and it will not automatically be triggered, if you launch your server. Therefore:
// In the JVM project's settings
resources in Compile += (fastOptJS in js).value.data
A couple of notes:
The first step is only necessary, if your web-server does only serve specific directories. Otherwise the second one is enough, as this adds the file to the resources already; where it lies is secondary.
Setting the crossTarget, as in #ochrons' answer will also output all the .class and .sjsir files in the resource directory.
Have a look at Vincent Munier's sbt-play-scalajs for out-of-the-box sbt-web / Scala.js integration (it follows a slightly different approach: It copies the file from the js project, rather than directly placing it in the JVM project. Useful if you have multiple JVM projects).
You can configure the Scala.js SBT plugin to output the JavaScript file in folder of your choosing. For example like this:
// configure a specific directory for scalajs output
val scalajsOutputDir = Def.settingKey[File]("directory for javascript files output by scalajs")
// make all JS builds use the output dir defined later
lazy val js2jvmSettings = Seq(fastOptJS, fullOptJS, packageJSDependencies) map { packageJSKey =>
crossTarget in(js, Compile, packageJSKey) := scalajsOutputDir.value
}
// instantiate the JVM project for SBT with some additional settings
lazy val jvm: Project = sharedProject.jvm.settings(js2jvmSettings: _*).settings(
// scala.js output is directed under "web/js" dir in the jvm project
scalajsOutputDir := (classDirectory in Compile).value / "web" / "js",
This will also store -jsdeps.js and .js.map files in the same folder, in case you want to use those in your web app.
For a more complete example, check out this tutorial which addresses many other issues of creating a more complex Scala.js application.

Cannot get sbt-concat to bundle styles from sbt-sass or sbt-less

(Example project provided) I cannot get sbt-concat to work as designed to find and concatenate stylesheets that result from styles that may be produced from preprocessor tasks. In my production app, I'm trying to use it to bundle select minified output files from sbt-sass. It does not work within the complex setup of that project, so I created an example project to see if I could get it to work at all. It does not work in the example project either. Here is a test project build.sbt that tries to create several bundles, with just about every possibility I can think of, just to see if any of them work (public Github repo, which you should be able to clone and immediately replicate the problem):
import com.typesafe.sbt.web.Import.WebKeys._
import com.typesafe.sbt.web.pipeline.Pipeline
name := """sbt-concat-test"""
version := "1.0-SNAPSHOT"
lazy val root = (project in file(".")).enablePlugins(PlayScala, SbtWeb)
scalaVersion := "2.11.1"
libraryDependencies ++= Seq(
jdbc,
anorm,
cache,
ws
)
resolvers += Resolver.sonatypeRepo("releases")
includeFilter in (Assets, LessKeys.less) := "*.less"
excludeFilter in (Assets, LessKeys.less) := "_*.less"
val myPipelineTask = taskKey[Pipeline.Stage]("Some pipeline task")
myPipelineTask := { mappings => println(mappings); mappings }
pipelineStages := Seq(myPipelineTask, concat)
Concat.groups := Seq(
"style-group1.css" -> group(sourceDirectory.value ** "*.css"),
"style-group2.css" -> group(baseDirectory.value ** "*.css"),
"style-group3.css" -> group((sourceDirectory in Assets).value ** "*.css"),
"style-group4.css" -> group(target.value ** "*.css"),
"style-group5.css" -> group(Seq("core.css", "styles/core.css", "assets/styles/core.css", "app/assets/styles/core.css")),
"style-group6.css" -> group(Seq("lessStyle.css", "ui/lessStyle.css", "styles/ui/lessStyle.css", "assets/styles/ui/lessStyle.css", "app/assets/styles/ui/lessStyle.css")),
"style-group7.css" -> group(Seq("sassStyle.css", "ui/sassStyle.css", "styles/ui/sassStyle.css", "assets/styles/ui/sassStyle.css", "app/assets/styles/ui/sassStyle.css")),
"style-group8.css" -> group(Seq("**/*.css"))
)
I run ; clean; reload; stage from activator to test. I see asset source files copied over into the target folder, with the following results for the declared bundles:
style-group1.css does not exist
style-group2.css contains the contents of button.css and core.css
style-group3.css contains the contents of core.css and button.css
style-group4.css does not exist
style-group5.css contains only the contents of core.css
style-group6.css contains only the contents of compiled lessStyle.scss
style-group7.css contains only the contents of compiled sassStyle.scss
style-group8.css does not exist
I do not understand why the 2nd and 3rd cases do not pick up the preprocessor-produced css files, yet the tailor-made 6th and 7th cases do. Perhaps notably, the result of myPipelineTask shows PathMappings for all source files, as well as the derived css and sourcemaps from the Sass and Less tasks.
According to Typesafe support, the source of my woes is the fact that sbt-concat implements its PathFinder logic in such a way as to only pick up assets that are verbatim the same filename as in the source directory. The sequence of relative filenames works for files in the target directory, but has no pattern matching. This is rather unfortunate.
What does work is to construct a Seq of output files that will exist post-compilation by using a PathFinder on the source directory. So for .scss files, something like:
Concat.groups := {
// Determine the output names of the style files to bundle
// This is really roundabout because sbt-concat only offers 2 ways of
// specifying files, relative paths and using a PathFinder, and the
// latter approach restricts itself to source files instead of output files.
val sourceDir = (sourceDirectory in Assets).value
val scssFiles = (sourceDir ** "*.scss").getPaths
val scssRelativePaths = scssFiles.map(_.stripPrefix(sourceDir.getPath).stripPrefix("/"))
val outputRelativePaths = scssRelativePaths.map(_.stripSuffix(".scss") + ".min.css")
Seq("bundle.min.css" -> group(outputRelativePaths))
}
As a side note, another quirk of sbt-concat is that it doesn't put its new files in its own directory in web-assets:assetsTarget to separate them from artifacts of other pipeline stages. The Concat.parentDir is also unnecessary because you can simply anything you would place in that variable as a prefix for your bundle file name directly.

Play Framework : how to not watch a folder for changes in Play Framework

I am using Play 2.2.3.
I am new to Play and SBT. I want to make Play not watch a folder during development. In this case, a node_modules folder under public folder.
I tried these below but it did not seems to work. Also, I dont' know what is the difference between watchSources and playMonitoredFiles.
.settings(
playMonitoredFiles <<= playMonitoredFiles map { (files: Seq[String]) =>
files.filterNot(file => file.contains("node_modules"))
}
)
.settings(
watchSources <<= watchSources map { (sources: Seq[java.io.File]) =>
sources
.filterNot(source => source.isFile && source.getPath.contains("node_modules") )
.filterNot(source => source.isDirectory && source.getPath.contains("node_modules"))
}
)
.settings(
watchSources := watchSources.value.filter { !_.getPath.contains("node_modules") }
)
Note: The original question was about a legacy release (2.2.3), however I found useful the same question for modern versions.
So basically watchSources is a SBT key used for the triggered-execution functionality.
On the contrary, playMonitoredFiles is a TaskKey defined by the PlayFramework sbt-plugin, which controls a set of directories to watch by
the development server (sbt run) for the auto-reloading functionality.
When launching an sbt run, for EVERY REQUEST, there is a check of the directories defined by playMonitoredFiles and if any change is found,
a silent re-compilation is triggered. This means shutting down the server, recompiling and starting again.
The default values for playMonitoredFiles are calculated in playMonitoredFilesTask.
So for example, we have a certain version file added to our resources, computed in each compilation. We needed PlayFramework to avoid checking changes on this file
on development, because on every-request, the auto-recompiling regenerated this file and play marked the sources to be recompiled again, however no java files were recompiled and
nothing is printed out, so we had to debug the application in order to find the issue.
This setting only applies to development server (sbt run).
Within a project settings in build.sbt, the task can be overridden to exclude a directory (in the example, target/version directory):
.settings(
...,
playMonitoredFiles := playMonitoredFiles.value.filterNot {
_.getPath() endsWith String.format("target%sversion", File.separator)
}
...,
)
The value is of type File and only contains directories.

Scala sbt - Cleaning files with a wildcard or regex

Suppose I have a Scala program that creates files ending in .foo.
I'm building with sbt and want to remove these files whenever sbt clean is called.
Add additional directory to clean task in SBT build shows that a singe file can be added by
cleanFiles <+= baseDirectory { _ / "test.foo" }
However, it's unclear how to extend this to do:
cleanFiles <append> <*.foo>
All .foo files will be in the same directory, so I don't need to recursively check directories.
Though, that would also be interesting to see.
How can I configure sbt to clean files matching a wildcard, or regex?
Is it a bad design decision to have sbt clean remove files my program generates?
Should I instead use a flag in my program? Using sbt clean seems cleaner to me rather
than having to call sbt clean then sbt "run --clean".
This will find anything that matches *.foo in the base directory (but not child directories):
cleanFiles <++= baseDirectory (_ * "*.foo" get)
This works because Seq[File] gets implicitly converted to a PathFinder, which has methods such as * (match the pattern in the base directory) and ** (match the pattern including child directories). Then get converts it back to a Seq[File].