通过 Play 中的“activator run”运行时获取要编译的资产

Posted

技术标签:

【中文标题】通过 Play 中的“activator run”运行时获取要编译的资产【英文标题】:Get assets to compile when running via "activator run" in Play 【发布时间】:2014-08-08 19:23:38 【问题描述】:

我使用 Sass 作为我的 CSS 预处理器,并且我试图让它通过资产管道运行。我尝试将此 sassTask 实现为源文件任务和 Web 资产任务,但两种方式都遇到了问题。

如果我将 Sass 作为源任务运行(见下文),它会在 activator run 期间被触发,当请求页面并在页面重新加载时找到更新的文件时。我遇到的问题是,生成的 CSS 文件都被直接转储到 target/web/public/main/lib,而不是反映在 resources-managed 目录下的子目录中。我不知道如何做到这一点。

相反,我尝试将 Sass 编译实现为 Web 资产任务(见下文)。以这种方式工作,据我所知,resources-managed 不起作用,所以我将我的文件直接编译成target/web/public/main/lib。我确定我没有足够动态地执行此操作,但我不知道如何做得更好。但这里最大的问题是管道在通过activator run 工作时显然没有运行。我可以使用activator stage 让它运行,但我确实需要它在常规开发工作流程中工作,以便我可以在开发服务器运行时更​​改样式文件,就像使用 Scala 文件一样。

我尝试过梳理这些论坛、sbt-web 文档和一些现有的插件,但我发现这个过程非常令人沮丧,因为 SBT 的复杂性和实际内容的不透明性发生在构建过程中。

Sass 编译作为源文件任务:

lazy val sassTask = TaskKey[Seq[java.io.File]]("sassTask", "Compiles Sass files")

sassTask := 
  import sys.process._
  val x = (WebKeys.nodeModules in Assets).value
  val sourceDir = (sourceDirectory in Assets).value
  val targetDir = (resourceManaged in Assets).value
  Seq("sass", "-I", "target/web/web-modules/main/webjars/lib/susy/sass", "--update", s"$sourceDir:$targetDir").!
  val sources = sourceDir ** "*.scss"
  val mappings = sources pair relativeTo(sourceDir)
  val renamed = mappings map  case (file, path) => file -> path.replaceAll("scss", "css") 
  val copies = renamed map  case (file, path) => file -> targetDir / path 
  copies map (_._2)


sourceGenerators in Assets <+= sassTask

将 Sass 编译为 Web 资产任务:

lazy val sassTask = taskKey[Pipeline.Stage]("Compiles Sass files")

sassTask := 
  (mappings: Seq[PathMapping]) =>
    import sys.process._
    val sourceDir = (sourceDirectory in Assets).value
    val targetDir = target.value / "web" / "public" / "main"
    val libDir = (target.value / "web" / "web-modules" / "main" / "webjars" / "lib" / "susy" / "sass").toString
    Seq("sass", "-I", libDir, "--update", s"$sourceDir:$targetDir").!
    val sources = sourceDir ** "*.scss"
    val mappings = sources pair relativeTo(sourceDir)
    val renamed = mappings map  case (file, path) => file -> path.replaceAll("scss", "css") 
    renamed


pipelineStages := Seq(sassTask)

【问题讨论】:

【参考方案1】:

我认为根据与Asset Pipeline 相关的文档,源文件任务是一种方法:

作为插件的源文件任务示例有 CoffeeScript、LESS 和 JSHint。其中一些获取源文件并生成目标网络 资产,例如CoffeeScript 生成 JS 文件。此类别中的插件 就其功能而言,它们是相互排斥的,即 只有一个 CoffeeScript 插件会使用 CoffeeScript 源,并且 生成目标 JS 文件。综上,源文件插件产生web 资产。

我认为你试图达到的目标属于这一类。

TL;DR; - build.sbt

val sassTask = taskKey[Seq[File]]("Compiles Sass files")

val sassOutputDir = settingKey[File]("Output directory for Sass generated files")

sassOutputDir := target.value / "web" / "sass" / "main"

resourceDirectories in Assets += sassOutputDir.value

sassTask := 
  val sourceDir = (sourceDirectory in Assets).value
  val outputDir = sassOutputDir.value
  val sourceFiles = (sourceDir ** "*.scss").get
  Seq("sass", "--update", s"$sourceDir:$outputDir").!
  (outputDir ** "*.css").get


sourceGenerators in Assets += sassTask.taskValue

说明

假设您在 app/assets/&lt;whatever&gt; 目录中有 sass 文件,并且您想在 web/public/main/&lt;whatever&gt; 目录中创建 css 文件,这就是您可以做的。

创建一个任务,它将读取app/assets/&lt;whatever&gt;目录和子目录中的文件,并输出到我们定义的sassOutputDir

val sassTask = taskKey[Seq[File]]("Compiles Sass files")

val sassOutputDir = settingKey[File]("Output directory for Sass generated files")

sassOutputDir := target.value / "web" / "sass" / "main"

resourceDirectories in Assets += sassOutputDir.value

sassTask := 
  val sourceDir = (sourceDirectory in Assets).value
  val outputDir = sassOutputDir.value
  val sourceFiles = (sourceDir ** "*.scss").get
  Seq("sass", "--update", s"$sourceDir:$outputDir").!
  (outputDir ** "*.css").get

但这还不够。如果要保留目录结构,则必须将 sassOutputDir 添加到 resourceDirectories in Assets。这是因为 sbt-web 中的映射是这样声明的:

mappings := 
  val files = (sources.value ++ resources.value ++ webModules.value) ---
    (sourceDirectories.value ++ resourceDirectories.value ++ webModuleDirectories.value)
  files pair relativeTo(sourceDirectories.value ++ resourceDirectories.value ++ webModuleDirectories.value) | flat

这意味着所有未映射的文件都使用alternativeflat 策略进行映射。但是解决方法很简单,只需将其添加到您的build.sbt

resourceDirectories in Assets += sassOutputDir.value

这将确保目录结构得以保留。

【讨论】:

天哪,老兄,你救了我的命。令人沮丧的是,我花了 48 小时试图弄清楚这一点,并且我阅读了整个 48 页的 SBT 教程。我想如果我阅读了整个文档,我最终可能会偶然发现这种行为,但我不得不说,SBT 幕后发生的所有“魔法”都非常令人沮丧。 @acjay 很高兴它对您有所帮助。如果你认为它是正确的,请你接受答案,所以问题不是没有答案。谢谢。 您使用sourceGenerators,但您可能是指resourceGenerators。无论如何,感谢您指出正确的方向。 @lpiepiora,非常感谢您的回答。这对我帮助很大!

以上是关于通过 Play 中的“activator run”运行时获取要编译的资产的主要内容,如果未能解决你的问题,请参考以下文章

Play 中的单元测试!不需要测试 Play Server 的框架应用程序

如何部署和运行 Play! 2.1 通过 Jenkins 到 EC2

PHP 中的 Google Play 商店评论 API

如何通过 java 为 Play!2.1.1 配置 ebean 服务器

如何处理 Play 框架中的可选查询参数

Play 2.2.1 中的 SSL\TLS 支持