Reputation: 2324
I currently have two projects; one for the server-side (Akka-HTTP) and one for the client-side (ScalaJS). The client-side generates the .js
files I want to serve using the server-side. To give the server-side access to those files, I added this build.sbt
entry in the server-side project:
lazy val server = project.in(file("server"))
...
.settings(
(resourceGenerators in Compile) <+= Def.task {
val f1 = (fullOptJS in client).value.data
val f1sm = f1.getParentFile / (f1.getName + ".map")
f1 :: f1sm :: (packageMinifiedJSDependencies in client).value :: Nil
}
)
This, however, puts my .js
and .map
files inside the root of the classes
folder. Since I want to serve my assets like so:
pathPrefix("assets") {
...
pathPrefix("client") {
getFromResourceDirectory("<directory where .js and .map files are placed>")
}
}
with emphasis on the #getFromResourceDirectory
method, this isn't going to work with my assets inside the root of my classes
folder. My application.conf
is placed in the same directory as the generated files which would make that available for the whole world to see.
What I would like is my .js
and .map
files inside a sub-directory of the classes
directory. Something like client/
so I can use the #getFromResourceDirectory
method like getFromResourceDirectory("client")
and don't worry my configuration file is available to the outside world.
My question - How do I achieve this?
I already tried adding:
lazy val client = project.in(file("client"))
...
.settings(inConfig(Compile)((fullOptJS :: packageMinifiedJSDependencies :: Nil)
.map(a => (crossTarget in a) ~= (_ / "client")))
)
to my client-side project which only places the generated .js
and .map
files in a client folder in the client-side project its scala-2.11
folder:
but the setting does not propagate to the server-side project its classes
folder:
Upvotes: 1
Views: 309
Reputation: 1
@Martijn, thx for the great solution.
This is real help me a lot. little update here.
for frontend/client settings.
.settings(inConfig(Compile)(
(fullOptJS :: fastOptJS :: packageScalaJSLauncher
:: packageJSDependencies :: packageMinifiedJSDependencies :: Nil)
.map(f => (crossTarget in f) ~= (_ / "sjsout"))
))
for backend/server settings.
.settings(
(resourceGenerators in Compile) += Def.task {
val fastJsOut = (fastOptJS in Compile in frontend).value.data
val fastJsSourceMap = fastJsOut.getParentFile / (fastJsOut.getName + ".map")
val fullJsOut = (fullOptJS in Compile in frontend).value.data
val fullJsSourceMap = fullJsOut.getParentFile / (fullJsOut.getName + ".map")
Seq(
fastJsOut,
fastJsSourceMap,
//fullJsOut,
//fullJsSourceMap,
(packageScalaJSLauncher in Compile in frontend).value.data,
(packageJSDependencies in Compile in frontend).value
//(packageMinifiedJSDependencies in Compile in frontend).value
)
}.taskValue,
(resourceDirectories in Compile) += (crossTarget in frontend).value,
watchSources ++= (watchSources in frontend).value
)
Upvotes: 0
Reputation: 2324
Diving into SBT let me down the following path; Firstly, I inspected server/compile:resourceGenerators
(using sbt inspect server/compile:resourceGenerators
):
[info] ...
[info] Dependencies:
[info] server/compile:discoveredSbtPlugins
[info] server/compile:resourceManaged
[info] client/compile:packageMinifiedJSDependencies
[info] client/compile:fullOptJS
[info] Reverse dependencies:
[info] server/compile:managedResources
[info] ...
I then inspected server/compile:managedResources
:
[info] ...
[info] Dependencies:
[info] server/compile:resourceGenerators
[info] Reverse dependencies:
[info] server/compile:resources
[info] ...
I then inspected server/compile:resources
:
[info] ...
[info] Dependencies:
[info] server/compile:managedResources
[info] server/compile:unmanagedResources
[info] Reverse dependencies:
[info] server/compile:copyResources
[info] ...
I then inspected server/compile:copyResources
:
[info] ...
[info] Defined at:
[info] (sbt.Defaults) Defaults.scala:295
[info] Dependencies:
[info] server/compile:classDirectory
[info] server/compile:resources
[info] server/compile:resourceDirectories
[info] server/compile:copyResources::streams
[info] ...
I then inspected the Defaults.scala
file from SBT on line 295:
copyResources <<= copyResourcesTask
I then inspected this copyResourcesTask
definition:
def copyResourcesTask =
(classDirectory, resources, resourceDirectories, streams) map { (target, resrcs, dirs, s) =>
val cacheFile = s.cacheDirectory / "copy-resources"
val mappings = (resrcs --- dirs) pair (rebase(dirs, target) | flat(target))
s.log.debug("Copy resource mappings: " + mappings.mkString("\n\t", "\n\t", ""))
Sync(cacheFile)(mappings)
mappings
}
The mappings
val is very important in this case. It creates the mappings between the paths created by the various tasks responsible for all the resources and the final path these resources will receive in the target
folder. The #rebase
method is (mostly) responsible for these mappings but it falls back on the #flat
method if not a single resourceDirectories
entry is in the path of the resource. By simply adding the crossTarget
path of my client-side project to the resourceDirectories
setting, rebase replaces everything up until the client/<filename>
part of my .js
and .map
files instead of falling-back on the #flat
method and only replacing the <filename>
part which, combined with this setting:
lazy val client = project.in(file("client"))
...
.settings(inConfig(Compile)((fullOptJS :: packageMinifiedJSDependencies :: Nil)
.map(a => (crossTarget in a) ~= (_ / "client")))
)
is exactly the behavior I want.
Add to your client-side project:
.settings(inConfig(Compile)((fullOptJS :: packageMinifiedJSDependencies :: Nil)
.map(a => (crossTarget in a) ~= (_ / "client")))
)
Add to your server-side project:
.settings(inConfig(Compile)((resourceGenerators <+= Def.task {
val f1 = (fullOptJS in client).value.data
val f1sm = f1.getParentFile / (f1.getName + ".map")
f1 :: f1sm :: (packageMinifiedJSDependencies in client).value :: Nil
}) :: (resourceDirectories += (crossTarget in client).value) :: Nil))
Upvotes: 3