Importing assets from an NPM Package - scala.js

Say I want to include font-awesome in my webapp. So I define my build.sbt as follows:
val commonSettings = Seq(
name := "repro",
version := "1.0",
scalaVersion := "2.12.8",
unmanagedSourceDirectories in Compile +=
baseDirectory.value / ".." / "shared" / "src" / "main" / "scala"
)
val client = project.in(file("client"))
.settings(commonSettings: _*)
.settings(
npmDependencies in Compile ++= Seq(
"font-awesome" -> "4.7.0",
),
mainClass in Compile := Some("app.App"),
scalaJSUseMainModuleInitializer := true,
webpackBundlingMode := BundlingMode.LibraryOnly(),
)
.enablePlugins(ScalaJSPlugin)
.enablePlugins(ScalaJSBundlerPlugin)
val server = project.in(file("server"))
.settings(commonSettings: _*)
.settings(
npmAssets ++= NpmAssets.ofProject(client) { nodeModules =>
(nodeModules / "font-awesome").allPaths
}.value
)
.enablePlugins(WebScalaJSBundlerPlugin)
Can I configure this project so that my "package" command will then include the css in my target/webapp folder? Or is there another command I have to use?

In addition to your configuration, you have to add the following settings to the server project:
.settings(
scalaJSProjects := Seq(client),
pipelineStages in Assets := Seq(scalaJSPipeline),
managedClasspath in Runtime += (packageBin in Assets).value,
WebKeys.packagePrefix in Assets := "public/"
)
The first line introduces a dependency between the server project and the assets produced by the client project. The scalaJSProjects settings is introduced by the sbt-web-scalajs plugin.
The second line integrates the assets produced by the client project into the Web assets managed by sbt-web.
The third line tells sbt to include the assets produced by the sbt-web plugin to the classpath of the server.
The last line is optional, it simply puts the produced assets into the public/ resource directory, so that they are not mixed with other classpath resources which are not meant to be exposed to the outside world.
With this configuration, you can build the production assets with the following command:
> server/web-assets:package
Or, from a build file, by using the packageBin in Assets task.
This will produce a target/scala-2.12/repro_2.12-1.0-web-assets.jar file containing the JavaScript bundle produced by Webpack on your client project, as well as the font-awesome/ directory.

Related

sbt plugin: add an unmanaged jar file

I'm trying to create a relatively simple sbt plugin to wrap grpc-swagger artifact.
Therefore, I've created a project with the following structure:
projectDir/
build.sbt
lib/grpc-swagger.jar <- the artifact I've downloaded
src/...
where build.sbt looks like the following:
ThisBuild / version := "0.0.1-SNAPSHOT"
ThisBuild / organization := "org.testPlugin"
ThisBuild / organizationName := "testPlugin"
lazy val root = (project in file("."))
.enable(SbtPlugin)
.settings(name := "grpc-swagger-test-plugin")
According to sbt docs, that's all I have to do in order to include an unmanaged dependecy, that is:
create a lib folder;
store the artifact in there;
However, when I do execute sbt compile publishLocal, the plugin published lacks of that external artifact.
So far I've tried to:
set exportJars := true flag
add Compile / unmanagedJars += file(lib/grpc-swagger.jar") (with also variations of the path)
manual fiddling to libraryDependecies using from file("lib/grpc-swagger.jar") specifier
but none so far seemed to work.
So how am I supposed to add an external artifact to a sbt plugin?
The proper solution to this problem is to publish the grpc-swagger library. If for whatever reason this can't be done from that library's build system, you can do it with sbt. Just add a simple subproject whose only job it is to publish that jar. It should work like so:
...
lazy val `grpc-swagger` = (project in file("."))
.settings(
name := "grpc-swagger",
Compile / packageBin := baseDirectory.value / "lib" / "grpc-swagger.jar",
// maybe other settings, such as grpc-swagger's libraryDependencies
)
lazy val root = (project in file("."))
.enable(SbtPlugin)
.settings(name := "grpc-swagger-test-plugin")
.dependsOn(`grpc-swagger`)
...
The pom file generated for the root project should now specify a dependency on grpc-swagger, and running the publish task in the grpc-swagger project will publish that jar along with a pom file.
That should be enough to make things work, but honestly, it's still a hack. The proper solution is to fix grpc-swagger's build system so you can publish an artifact from there and then just use it via libraryDependencies.

Create target directory outside project directory with sbt

The structure of my project is:
└───current-working-dir
├───my-project
├───src
When I run sbt compile in current-working-dir where .sbt file looks like:
lazy val root = (project in file("my-project"))
.settings(
name := "my-project",
settings,
libraryDependencies ++= commonDependencies
)
then it generates target in the project directory:
└───current-working-dir
├───my-project
├───src
├───target
Is it possible to make sbt to create target outside the project directory? For instance:
└───current-working-dir
├───my-project
├───src
├───target
or
└───current-working-dir
├───my-project
├───src
├───my-project-compilation
├───target
target setting controls where files are generated. Here is an example how to change it
target := baseDirectory.value / "my-project-compilation" / "target"
Actually, I guess correct is
lazy val root = (project in file("my-project"))
.settings(
name := "my-project",
settings,
libraryDependencies ++= commonDependencies,
target := baseDirectory.value / ".." / "my-project-compilation" / "target"
)
You can check that if you place file as current-working-dir/my-project/src/main/scala/App.scala then compiled class appears as current-working-dir/my-project-compilation/target/scala-2.13/classes/App.class.

How to a reference the shared source without depending on a project

Say I have a project defined as follows:
val commonSettings = Seq(
name := "project1",
version := "1.0",
scalaVersion := "2.12.8",
unmanagedSourceDirectories in Compile +=
baseDirectory.value / ".." / "shared" / "src" / "main" / "scala"
)
val client = project.in(file("client"))
.settings(commonSettings: _*)
val server = project.in(file("server"))
.settings(commonSettings: _*)
And I have a second project that uses a REST api to communicate with this server. Thus the second project uses code defined in the shared source of the first. The second project cannot depend on the entire project1 as there are incompatibilities between the sbt versions for the two projects.
What do I need to add to my build.sbt and/or change in the first project in order for the second project to reference the shared source?
If project1 and project 2 are finally going to run independently it would be better if you write REST APIs to communicate in between them.
You can write an API in project2 and hit it from project1 to run the shared code of project2.
Otherwise, why not just replicate shared code from project2 to project1 ?
You can try jitpack, add below lines in your build.sbt to enable jitpack resolver and add appropriate dependencies
resolvers += "jitpack" at "https://jitpack.io"
// you can also use specific sha or tag instead of master-SNAPSHOT
libraryDependencies += "org.xyz" %% "project" % "master-SNAPSHOT"

Multi module Scala Play 2.3 conf location

I've been tasked with rewriting an old ant build script to SBT. As it happens, our suite is built up of 3 modules:
A Play 2.3 front-end webserver;
A back-end for retrieving data from various other systems;
A middle module containing some shared classes for database access and business logic.
Below an excerpt of my Build.scala file can be found:
val sharedSettings = Seq(
organization := <organization here>,
version := "1.2.5",
scalaVersion := "2.11.1",
libraryDependencies ++= libraries,
unmanagedJars in Compile ++= baseDirectory.value / "lib",
unmanagedJars in Compile ++= baseDirectory.value / "src",
unmanagedJars in Compile ++= baseDirectory.value / "test"
)
lazy val middle = project.settings(sharedSettings: _*)
lazy val back = project.settings(sharedSettings: _*).dependsOn(middle)
lazy val front =
project
.enablePlugins(play.PlayScala)
.settings(sharedSettings: _*)
.settings(scalaSource in Compile := baseDirectory.value / "app")
.settings(
routesImport ++= Seq(
"scala.language.reflectiveCalls", // Removes warnings when using multiple routes files
"com.asml.cerberus.front.toolbox.Binders._")
)
.dependsOn(middle % "compile->compile;test->test")
I've got my application.conf in the ./front/conf/ directory. Unfortunately, if I now run sbt, it looks for a ./conf/application.conf file. (I've tested this by moving the conf directory.)
Is there any way how I can tell SBT/Play to use the front module's conf directory in stead?
In case that helps, we have a wrapper script around activator (sbt) activatorWrapper:
#!/bin/bash
activator -Dconfig.file=front/conf/application.conf
Then you can start your application with :
$ ./activatorWrapper
You can use system properties to specify an alternative config file. See here for the details.

How to copy resources with scala + play + sbt

I am using sbt [0.13] to compile a play [2.2] project using scala [2.10.3]. I have .sql files and scala files used for database migrations. The directory structure looks like:
app
|-> db
|-> migration
|-> V1__init.scala
|-> V2__newTable.sql
When I run compile from the play console (REPL), the scala file (V1__init.scala) is compiled to a .class and copied to the classes folder. But the .sql file is not moved.
I tried adding unmanagedResourceDirectories in Compile <++= baseDirectory { dir => Seq(dir/"app/db/migration") ++ Seq(dir/"db/migration") } but it did not copy the files. The whole block looks like:
val main = play.Project(appName, appVersion, appDependencies).settings(
scalaVersion := "2.10.3",
scalacOptions ++= Seq("-feature"), // enable feature warnings
unmanagedResourceDirectories in Compile <++= baseDirectory { dir => Seq(dir/"app/db/migration") ++ Seq(dir/"db/migration") }
)
I also tried using copyResources, but couldn't get that to work. Described here: http://www.playframework.com/documentation/2.0/SBTSettings
So does anyone know how I can copy the sql files to the classes folder?
Thanks!
UPDATE
I got IO.copyDirectory(new java.io.File("app/db/migration"), new java.io.File("target/scala-2.10/classes/db/migration"), true) to copy the files, but the destination is hard-coded and will change when I update scala
val main = play.Project(appName, appVersion, appDependencies).settings(
scalaVersion := "2.10.3",
scalacOptions ++= Seq("-feature"), // enable feature warnings
unmanagedResourceDirectories in Compile <+= scalaSource in Compile,
excludeFilter in unmanagedResources in Compile := "*.scala" || "*.java"
)
You can check easily the contents in the class folder with:
sbt clean full-classpath && ls target/scala-2.10/classes/db/migration/