Skip to content

Commit

Permalink
Add LiteServerPlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
jokade committed Apr 5, 2017
1 parent 468d7b0 commit 5f11129
Show file tree
Hide file tree
Showing 7 changed files with 287 additions and 11 deletions.
6 changes: 5 additions & 1 deletion plugin/src/main/scala/de/surfice/sbtnpm/NpmPlugin.scala
Expand Up @@ -29,6 +29,9 @@ object NpmPlugin extends AutoPlugin {
val npmTargetDir: SettingKey[File] =
settingKey[File]("Target directory for node_modules")

val npmNodeModulesDir: SettingKey[File] =
settingKey("Path to the node_modules dir")

/**
* List of the NPM packages (name and version) your application depends on.
* You can use [semver](https://docs.npmjs.com/misc/semver) versions:
Expand Down Expand Up @@ -75,6 +78,8 @@ object NpmPlugin extends AutoPlugin {
override lazy val projectSettings: Seq[Def.Setting[_]] = Seq(
npmTargetDir := baseDirectory.value,

npmNodeModulesDir := npmTargetDir.value / "node_modules",

npmPackageJsonFile := npmTargetDir.value / "package.json",

npmDependencies := Nil,
Expand All @@ -91,7 +96,6 @@ object NpmPlugin extends AutoPlugin {
),

npmWritePackageJson := {
import Cache._
val file = npmPackageJsonFile.value
val lastrun = npmWritePackageJson.previous
if(lastrun.isEmpty || lastrun.get.needsUpdateComparedToConfig(baseDirectory.value)) {
Expand Down
@@ -0,0 +1,169 @@
// Project: sbt-node
// Module:
// Description:
package de.surfice.sbtnpm.liteserver

import sbt._
import Keys._
import de.surfice.sbtnpm.{NpmPlugin, utils}
import de.surfice.sbtnpm.utils.{FileWithLastrun, JsonNode, NodeCommand}
import org.scalajs.sbtplugin.{ScalaJSPluginInternal, Stage}

object LiteServerPlugin extends AutoPlugin {

override lazy val requires = NpmPlugin


/**
* @groupname tasks Tasks
* @groupname settings Settings
*/
object autoImport {
val liteServerVersion: SettingKey[String] =
settingKey("Version of lite-server to be used")

val liteServerConfigFile: SettingKey[File] =
settingKey("Path to the node lite-server config file (scoped to fastOptJS or fullOptJS)")

val liteServerBaseDir: SettingKey[File] =
settingKey("Base directory from which files are served (scoped to fastOptJS or fullOptJS)")

val liteServerIndexFile: SettingKey[File] =
settingKey("Path to the index.html file (scoped to fastOptJS or fullOptJS")

val liteServerRoutes: SettingKey[Iterable[(String,String)]] =
settingKey("Entries to be put in the lite-server config 'routes' object (scoped to fastOptJS or fullOptJS)")

val liteServerCmd: SettingKey[NodeCommand] =
settingKey("command to run the node lite-server")

val liteServerWriteConfigFile: TaskKey[FileWithLastrun] =
taskKey("Create the lite-server config file for the current stage (fastOptJS or fullOptJS)")

val liteServerStart: TaskKey[Unit] =
taskKey("Start the node lite-server for the current stage (fastOptJS or fullOptJS")

val liteServerStop: TaskKey[Unit] =
taskKey("Stops the node lite-server for the current stage (fastOptJS or fullOptJS")

val liteServerWriteIndexFile: TaskKey[File] =
taskKey("Creates the index.html file for the current stage (fastOptJS or fullOptJS")
}

import autoImport._
import NpmPlugin.autoImport._

override lazy val projectSettings: Seq[Def.Setting[_]] = Seq(
liteServerVersion := "~2.3.0",

liteServerCmd := NodeCommand(npmNodeModulesDir.value,"lite-server","lite-server"),

npmDevDependencies += "lite-server" -> liteServerVersion.value
) ++
perScalaJSStageSettings(Stage.FullOpt) ++
perScalaJSStageSettings(Stage.FastOpt)


private def perScalaJSStageSettings(stage: Stage): Seq[Def.Setting[_]] = {

val stageTask = ScalaJSPluginInternal.stageKeys(stage)

Seq(
defineLiteServerConfigFile(stageTask),
defineLiteServerBaseDir(stageTask),
defineLiteServerIndexFile(stageTask),
defineLiteServerRoutes(stageTask),
defineLiteServerWriteConfigFile(stageTask),
defineLiteServerWriteIndexFile(stageTask),
defineLiteServerStart(stageTask),
defineLiteServerStop(stageTask)
)
}

private def defineLiteServerConfigFile(scoped: Scoped) =
liteServerConfigFile in scoped := utils.fileWithScalaJSStageSuffix( (crossTarget in (Compile,scoped)).value,"bs-config-",scoped,".json")

private def defineLiteServerBaseDir(scoped: Scoped) =
liteServerBaseDir in scoped := (crossTarget in (Compile,scoped)).value

private def defineLiteServerIndexFile(scoped: Scoped) =
liteServerIndexFile in scoped := utils.fileWithScalaJSStageSuffix((resourceDirectory in (Compile,scoped)).value,"index-",scoped,".html")

private def defineLiteServerRoutes(scoped: Scoped) =
liteServerRoutes in scoped := Seq("/node_modules/" -> npmNodeModulesDir.value.getAbsolutePath)

private def defineLiteServerWriteConfigFile(scoped: Scoped) =
liteServerWriteConfigFile in scoped := {
val lastrun = (liteServerWriteConfigFile in scoped).previous
val file = (liteServerConfigFile in scoped).value
val baseDir = (liteServerBaseDir in scoped).value
val indexFile = (liteServerWriteIndexFile in scoped).value
val indexPath = indexFile.relativeTo(baseDir) match {
case Some(p) => p.getPath()
case None =>
streams.value.log.error(s"index file $indexFile must be a child of base directory $baseDir")
""
}


if(lastrun.isEmpty || lastrun.get.needsUpdateComparedToConfig(baseDirectory.value)) {
writeConfigFile(file,
baseDir.getAbsolutePath,
indexPath,
(liteServerRoutes in scoped).value)
FileWithLastrun(file)
}
else lastrun.get
}

private def defineLiteServerStart(scoped: Scoped) =
liteServerStart in scoped := {
npmInstall.value
(liteServerWriteConfigFile in scoped).value
val cmd = liteServerCmd.value
val configFile = (liteServerConfigFile in scoped).value

cmd.startAndStore("-c",configFile.getAbsolutePath)(scoped,streams.value.log)
}

private def defineLiteServerStop(scoped: Scoped) =
liteServerStop in scoped := {
liteServerCmd.value.destroy(scoped,streams.value.log)
}

private def defineLiteServerWriteIndexFile(scoped: Scoped) =
liteServerWriteIndexFile in scoped := {
val src = (liteServerIndexFile in scoped).value
val dest = utils.fileWithScalaJSStageSuffix((liteServerBaseDir in scoped).value,"index-",scoped,".html")
IO.copy(Seq((src,dest)),overwrite = true)
dest
}

// private def defineLiteServer(stageTask: TaskKey[Attributed[File]]) =
// liteServer in stageTask := {
// npmInstall.value
// (liteServerWriteConfigFile in stageTask).value
// (stageTask in Compile).value
//
// val cmd = liteServerCmd.value
// val configFile = (liteServerConfigFile in stageTask).value
// val cwd = npmTargetDir.value
// cmd.run("-c",configFile.getAbsolutePath)(cwd,streams.value.log)
// cmd.start("-c",configFile.getAbsolutePath)(streams.value.log,waitAndKillOnInput = true)
//
//
// }

private def writeConfigFile(file: File, baseDir: String, indexFile: String, routes: Iterable[(String,String)]) = {
import JsonNode._
val config = Obj(
'server -> Obj(
'baseDir -> baseDir,
'index -> indexFile,
'routes -> Obj(routes)
)
)
IO.write(file,config.toJson)
}

}
Expand Up @@ -50,7 +50,7 @@ object SassPlugin extends AutoPlugin {
override lazy val projectSettings: Seq[Def.Setting[_]] = Seq(
nodeSassVersion := "~4.5.2",

nodeSassCmd := NodeCommand(npmTargetDir.value,"node-sass","node-sass"),
nodeSassCmd := NodeCommand(npmNodeModulesDir.value,"node-sass","node-sass"),

defineSassSourceDirectories(Compile),

Expand Down
Expand Up @@ -6,7 +6,7 @@ package de.surfice.sbtnpm.systemjs
import sbt._
import Keys._
import Cache._
import de.surfice.sbtnpm.NpmPlugin
import de.surfice.sbtnpm.{NpmPlugin, utils}
import de.surfice.sbtnpm.utils.FileWithLastrun
import org.scalajs.sbtplugin.{ScalaJSPluginInternal, Stage}

Expand All @@ -22,8 +22,11 @@ object SystemJSPlugin extends AutoPlugin {
val systemJSFile: SettingKey[File] =
settingKey("Path to the systemjs.config.js file (scoped to fastOptJS or fullOptJS)")

val systemJSPaths: SettingKey[Iterable[(String,String)]] =
settingKey("Entries to be put into the System.js config 'paths' object")

val systemJSMappings: SettingKey[Iterable[(String,String)]] =
settingKey("Entries to be put into the System.js 'map' object (the key 'app' is assigned automatically!)")
settingKey("Entries to be put into the System.js config 'map' object (the key 'app' is assigned automatically!)")

val systemJSPackages: SettingKey[Iterable[(String,SystemJSPackage)]] =
settingKey("System.js package definitions (the package 'app' is defined automatically!)")
Expand All @@ -33,6 +36,7 @@ object SystemJSPlugin extends AutoPlugin {
}

import autoImport._
import NpmPlugin.autoImport._

override lazy val projectSettings: Seq[Def.Setting[_]] = Seq(
) ++
Expand All @@ -46,6 +50,7 @@ object SystemJSPlugin extends AutoPlugin {

Seq(
defineSystemJSFile(stageTask),
defineSystemJSPaths(stageTask),
defineSystemJSMappings(stageTask),
defineSystemJSPackages(stageTask),
defineSystemJSTask(stageTask)
Expand All @@ -54,9 +59,20 @@ object SystemJSPlugin extends AutoPlugin {

private def defineSystemJSFile(scope: Any) = scope match {
case scoped: Scoped =>
systemJSFile in scoped := (crossTarget in (Compile,scoped)).value / s"systemjs-${scoped.key.toString.toLowerCase}.config.js"
systemJSFile in scoped := utils.fileWithScalaJSStageSuffix((crossTarget in (Compile,scoped)).value,"systemjs-",scoped,".config.js")
}

private def defineSystemJSPaths(scoped: Scoped) =
systemJSPaths in scoped := Seq(
"npm" -> {
val node_modules = npmNodeModulesDir.value
// val config = (systemJSFile in scoped).value
// println(node_modules)
// println(config)
node_modules.getAbsolutePath
}
)

private def defineSystemJSMappings(scoped: Scoped) =
systemJSMappings in scoped := Seq( "app" -> (crossTarget in (Compile,scoped)).value.relativeTo(baseDirectory.value).get.getPath )

Expand All @@ -74,6 +90,7 @@ object SystemJSPlugin extends AutoPlugin {
if(lastrun.isEmpty || file.needsUpdateComparedToConfig(baseDirectory.value)) {
writeSystemJSFile(
file = file.file,
paths = (systemJSPaths in scoped).value,
mappings = (systemJSMappings in scoped).value,
packages = (systemJSPackages in scoped).value)
new java.util.Date().getTime
Expand All @@ -83,15 +100,19 @@ object SystemJSPlugin extends AutoPlugin {
}

private def writeSystemJSFile(file: File,
paths: Iterable[(String,String)],
mappings: Iterable[(String,String)],
packages: Iterable[(String,SystemJSPackage)]): Unit = {
val js =
s"""(function (global) {
| System.config({
| paths: {
|${mappings.map(kv => " '"+kv._1+"': '"+kv._2+"'").mkString(",\n")}
|${paths.map(kv => " '"+kv._1+"': '"+kv._2+"'").mkString(",\n")}
| },
| map: {
|${mappings.map(kv => " '"+kv._1+"': '"+kv._2+"'").mkString(",\n")}
| },
| packages: {
|${packages.map(kv => " '"+kv._1+"': "+kv._2.toJS(" ")).mkString(",\n")}
| }
| });
Expand Down
Expand Up @@ -3,7 +3,7 @@
// Description:
package de.surfice.sbtnpm.utils

import sbt._
import sbt.{Level, _}

/**
* Helper for platform-independent handling of external commands.
Expand All @@ -13,14 +13,51 @@ trait ExternalCommand {
ExternalCommand.execute(cmdPath +: args, workingDir, logger)
}

def start(args: String*)(logger: Logger, waitAndKillOnInput: Boolean = false, connectInput: Boolean = false): Process =
ExternalCommand.start(cmdPath +: args, logger, waitAndKillOnInput, connectInput)

def startAndStore(args: String*)(key: Any, logger: Logger): Process = ExternalCommand.storedProcesses.get((this,key)) match {
case Some(p) =>
logger.warn(s"process $cmdPath already running")
p
case None =>
logger.info(s"starting $cmdPath")
ExternalCommand.storeProcess((this,key),start(args:_*)(logger))
}

def destroy(key: Any, logger: Logger): Option[Process] = ExternalCommand.destroyProcess((this,key)) match {
case None =>
logger.warn(s"process $cmdPath not running")
None
case x =>
logger.info(s"stopped $cmdPath")
x
}

def cmdPath: String
}


object ExternalCommand {
private var _processes = Map.empty[Any,Process]
def storeProcess(key: Any, process: Process): Process = this.synchronized{
if(_processes.contains(key))
throw new RuntimeException(s"There is already a process with defined for key $key")
_processes += key -> process
process
}
def storedProcesses: Map[Any,Process] = _processes
def destroyProcess(key: Any): Option[Process] = this.synchronized{
_processes.get(key) map { p =>
p.destroy()
_processes -= key
p
}
}

def apply(cmdName: String): ExternalCommand = new Impl(cmdName)

class Impl(val cmdPath: String) extends ExternalCommand
case class Impl(cmdPath: String) extends ExternalCommand

def execute(cmdLine: Seq[String], cwd: sbt.File, logger: Logger): Unit = {
val ret = Process(cmdLine) ! logger
Expand All @@ -30,7 +67,26 @@ object ExternalCommand {

}

def start(cmdLine: Seq[String], logger: Logger, waitAndKillOnInput: Boolean, connectInput: Boolean): Process = {
val p = Process(cmdLine).run(logger,connectInput)
if(waitAndKillOnInput) {
readLine("\nPress RETURN to stop external command\n\n")
p.destroy()
}
p
}

object npm extends Impl("npm") {
def install(npmTargetDir: File, logger: Logger): Unit = run("install")(npmTargetDir,logger)
class NpmLogger(wrapped: Logger) extends Logger {
override def trace(t: => Throwable): Unit = wrapped.trace(t)
override def success(message: => String): Unit = wrapped.success(message)
override def log(level: Level.Value, message: => String): Unit = (level,message) match {
case (Level.Error,msg) if msg.startsWith("npm WARN") =>
wrapped.log(Level.Warn,message.stripPrefix("npm WARN "))
case (lvl,msg) =>
wrapped.log(lvl,msg)
}
}
def install(npmTargetDir: File, logger: Logger): Unit = run("install")(npmTargetDir,new NpmLogger(logger))
}
}
Expand Up @@ -5,7 +5,7 @@ package de.surfice.sbtnpm.utils

import sbt._

case class NodeCommand(nodeTargetDir: File, pkg: String, cmdName: String) extends ExternalCommand {
override lazy val cmdPath = (nodeTargetDir.getAbsoluteFile / "node_modules" / pkg / "bin" / cmdName).toString
case class NodeCommand(nodeModulesDir: File, pkg: String, cmdName: String) extends ExternalCommand {
override lazy val cmdPath = (nodeModulesDir.getAbsoluteFile / pkg / "bin" / cmdName).toString
}

0 comments on commit 5f11129

Please sign in to comment.