How can sbt-web output be used with xsbt-web-plugi

2020-06-06 04:01发布

问题:

I am trying to use the sbt-web plugin without the play framework and instead build a webapp using the xsbt-web-plugin.

I have gotten the sbt-web plugin working correctly in handling the asset pipeline and have it creating a valid webjar output (via packageBin) as well as the standard "web/public/main" output (via assets).

Separately, I have been using the xsbt-web-plugin to develop a webapp and serve that webapp from within SBT (via container:start). The webapp project can consume webjar dependencies from mavenCentral and refer to those resources without issue.

What I have not been able to figure out is how can I get the xsbt-web-plugin to include assets coming out of the sbt-web pipeline in the web application. It seems that the best I can do is to get them into the CLASSPATH. (From what I understand, this is all that play needs, because they have an "Asset Controller" that serves those assets out of the CLASSPATH and therefore doesn't need them to be available as static assets to the web application).

I have made a public GitHub repository (https://github.com/MartinSnyder/serving-sbt-web-output) that demonstrates what I am trying to do.

My plugins.sbt is:

resolvers += Resolver.typesafeRepo("releases")

addSbtPlugin("com.typesafe.sbt" % "sbt-web" % "1.1.1")

addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6")

addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "1.0.0")

My build.sbt is:

name := "Example project serving sbt-web output from within SBT"

organization in ThisBuild := "com.martinsnyder"

version in ThisBuild := "0.0.1"

scalaVersion in ThisBuild := "2.11.6"

lazy val example_webjar =
  project
    .in(file("example_webjar"))
    .settings(libraryDependencies ++= Seq("org.webjars" % "requirejs" % "2.1.16"))
    .enablePlugins(SbtWeb)

lazy val example_webapp =
  project
    .in(file("example_webapp"))
    .dependsOn(example_webjar)
    .settings(libraryDependencies ++= Seq(
      "javax.servlet" % "servlet-api" % "2.5" % "provided",
      "org.eclipse.jetty" % "jetty-webapp" % "9.3.0.M2" % "container",
      "org.eclipse.jetty" % "jetty-plus"   % "9.3.0.M2" % "container",
      "commons-logging" % "commons-logging" % "1.2" % "container"
    ))
    .enablePlugins(SbtWeb)
    .settings(jetty(): _*)

The HTML file in the webapp is:

<html>
    <head>
        <title>Example</title>
        <link rel="stylesheet" type="text/css" href="css/main.css">
        <link rel="stylesheet" type="text/css" href="lib/example_webjar/css/main.css">
        <link rel="stylesheet" type="text/css" href="webjar/example_webjar/0.0.1/css/main.css">
        <script src="webjars/requirejs/2.1.16/require.js"></script>
    </head>
    <body>
        <div class="red">Red</div>
        <div class="green">Green</div>
    </body>
</html>

As things currently stand, requirejs is served successfully, as it is coming from a pre-build webjar. The three tags are all different and failing attempts to reference the asset output from sbt-web.

The best-case scenario I am trying to achieve is to get the sbt-web plugin output(target/web/public/main/) included in the xsbt-web-plugin webapp output (target/webapp/). I would settle for the xsbt-web-plugin being able to access the project dependency as a webjar.

回答1:

If you tell xsbt-web-plugin to use sbt-web's output as its Web application resources directory, it should get you most of the way.

Just add webappSrc in webapp <<= WebKeys.assets in Assets as a setting to your example_webapp project:

lazy val example_webapp =
  project
    .in(file("example_webapp"))
    .dependsOn(example_webjar)
    .settings(libraryDependencies ++= Seq(
      "javax.servlet" % "servlet-api" % "2.5" % "provided",
      "org.eclipse.jetty" % "jetty-webapp" % "9.3.0.M2" % "container",
      "org.eclipse.jetty" % "jetty-plus"   % "9.3.0.M2" % "container",
      "commons-logging" % "commons-logging" % "1.2" % "container"
    ))
    .enablePlugins(SbtWeb)
    .settings(jetty(): _*)
    .settings(webappSrc in webapp <<= WebKeys.assets in Assets)


回答2:

Until that issue has been resolved, the following configuration might be useful:

// To sbt-web: Use 'src/main/webapp' for asset sources
sourceDirectory in Assets := baseDirectory.value / "src" / "main" / "webapp"

// To xsbt-web-plugin: Use sbt-web output instead of 'src/main/webapp'
webappSrc in webapp <<= WebKeys.assets in Assets

This will allow you to use src/main/webapp for both your assets and normal config files (e.g. web.xml).



回答3:

Another alternative would be to use webappPostProcess as recommended in https://github.com/earldouglas/xsbt-web-plugin/issues/228. This will allow you to retain your existing project structure.

The following example would copy everything produced by sbt-web into the "assets" directory of your war (and overwrite any duplicate files in the target directory):

webappPostProcess := {
  import Path._

  val assets = (Compile / WebKeys.assets).value

  webapp =>
    val tocopy = (assets ** ("*" -- HiddenFileFilter)).get()
    val pairs = tocopy pair rebase(assets, webapp / "assets")
    IO.copy(pairs, true, true, false)
}