Permalink
Browse files

Adds initial version of the plugin

  • Loading branch information...
0 parents commit 34a2a2756d37a5d803f66dd4cf8978e690d39c9a @casualjim casualjim committed Jul 20, 2012
Showing with 15,931 additions and 0 deletions.
  1. +65 −0 .gitignore
  2. +16 −0 LICENSE
  3. +77 −0 README.md
  4. +67 −0 build.sbt
  5. +1 −0 project/build.properties
  6. +5 −0 project/plugins.sbt
  7. +15,563 −0 src/main/resources/requirejs/r.js
  8. +137 −0 src/main/scala/org/scalatra/requirejs/RequireJsPlugin.scala
65 .gitignore
@@ -0,0 +1,65 @@
+## generic files to ignore
+*~
+*.lock
+*.DS_Store
+*.swp
+*.out
+
+# rails specific
+*.sqlite3
+config/database.yml
+log/*
+tmp/*
+
+# java specific
+*.class
+
+# python specific
+*.pyc
+
+# xcode/iphone specific
+build/*
+*.pbxuser
+*.mode2v3
+*.mode1v3
+*.perspective
+*.perspectivev3
+*~.nib
+
+# akka specific
+logs/*
+
+# sbt specific
+project/boot
+lib_managed/*
+project/build/target
+project/build/lib_managed
+project/build/src_managed
+project/plugins/lib_managed
+project/plugins/target
+project/plugins/src_managed
+project/plugins/project
+
+core/lib_managed
+core/target
+pubsub/lib_managed
+pubsub/target
+
+# eclipse specific
+.metadata
+jrebel.lic
+.idea/workspace.xml
+.idea/bashsupport_project.xml
+
+*.iml
+*.ipr
+*.iws
+
+target
+.idea
+.settings
+.classpath
+.project
+.ensime*
+*.sublime-*
+.cache
16 LICENSE
@@ -0,0 +1,16 @@
+The MIT License (MIT)
+Copyright (c) 2011 Scalatra.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+and associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
77 README.md
@@ -0,0 +1,77 @@
+## SBT require.js
+
+A plugin to run r.js from sbt which hooks into the compile task.
+
+At this stage the plugin shells out to node.js so it does require node.js to be installed on the machine compiling the application.
+
+[Installing Node.js](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager)
+
+Adding the plugin:
+
+```scala
+addSbtPlugin("org.scalatra.requirejs" % "sbt-requirejs" % "0.0.1")
+```
+
+Using the plugin:
+
+*build.sbt*
+
+```scala
+import RequireJsKeys._
+
+seq(requireJsSettings: _*)
+
+buildProfile in (Compile, requireJs) := (
+ ("uglify" -> ("ascii_only" -> true)) ~
+ ("pragmasOnSave" -> ("excludeCoffeeScript" -> true) ~ ("excludeJade" -> true)) ~
+ ("paths" -> ("jquery" -> "empty:")) ~
+ ("stubModules" -> List("cs", "jade")) ~
+ ("modules" -> List[JValue](("name" -> "main") ~ ("exclude" -> List("coffee-script", "jade"))))
+)
+
+baseUrl in (Compile, requireJs) := "js"
+
+mainConfigFile in (Compile, requireJs) <<=
+ (sourceDirectory in (Compile, requireJs), baseUrl in (Compile, requireJs))((a, b) => Some(a / b / "main.js"))
+```
+
+*Defaults*
+
+```scala
+// The location to put the compiled requirejs application
+webApp in requireJs <<= (sourceDirectory in c)(_ / "webapp")
+
+// The location of the source files for the require.js app
+sourceDirectory in requireJs <<= (sourceDirectory in c)(_ / "requirejs")
+
+// The location of the r.js file
+rjs in requireJs <<= (target in c)(_ / "r.js")
+
+// The location of the node.js binary
+nodeBin in requireJs := ("which node" !!).trim
+
+// A JSON file to use as source for the require js build profile
+buildProfileFile in requireJs <<= (baseDirectory in c)(_ / "project" / "requirejs.build.json")
+
+// A lift-json JValue object to use as r.js build profile (overrides settings defined in buildProfileFile)
+buildProfile in requireJs := JNothing
+
+// The generated profile file for r.js to actually use when running the optimizer
+buildProfileGenerated in requireJs <<= (target in c)(_ / "requirejs.build.js")
+
+// The working directory for the optimizer to do its work.
+target in requireJs <<= (target in c)(_ / "requirejs")
+
+// The baseUrl to use in the build profile
+baseUrl in requireJs := "scripts"
+
+// The main config file to use with the path definitions (this comes from your app)
+mainConfigFile in requireJs := None
+
+// files to include when copying from the optimization step to the webapp
+includeFilter in requireJs := "*"
+
+// files to exclude when copying from the optimization step to the webapp
+excludeFilter in requireJs := "build.txt" || (".*" - ".") || "_*" || HiddenFileFilter
+```
+
67 build.sbt
@@ -0,0 +1,67 @@
+import xml.Group
+
+sbtPlugin := true
+
+name := "sbt-requirejs"
+
+organization := "org.scalatra.requirejs"
+
+version := "0.0.1-SNAPSHOT"
+
+libraryDependencies ++= Seq(
+ "net.liftweb" %% "lift-json" % "2.4"
+)
+
+publishMavenStyle := true
+
+publishTo <<= version { (v: String) =>
+ val nexus = "https://oss.sonatype.org/"
+ if (v.trim.endsWith("SNAPSHOT"))
+ Some("snapshots" at nexus + "content/repositories/snapshots")
+ else
+ Some("releases" at nexus + "service/local/staging/deploy/maven2")
+}
+
+publishArtifact in Test := false
+
+pomIncludeRepository := { x => false }
+
+packageOptions <<= (packageOptions, name, version, organization) map {
+ (opts, title, version, vendor) =>
+ opts :+ Package.ManifestAttributes(
+ "Created-By" -> "Simple Build Tool",
+ "Built-By" -> System.getProperty("user.name"),
+ "Build-Jdk" -> System.getProperty("java.version"),
+ "Specification-Title" -> title,
+ "Specification-Vendor" -> "Scalatra",
+ "Specification-Version" -> version,
+ "Implementation-Title" -> title,
+ "Implementation-Version" -> version,
+ "Implementation-Vendor-Id" -> vendor,
+ "Implementation-Vendor" -> "Scalatra",
+ "Implementation-Url" -> "https://github.com/scalatra/sbt-requirejs"
+ )
+}
+
+homepage := Some(url("https://github.com/scalatra/sbt-requirejs"))
+
+startYear := Some(2012)
+
+licenses := Seq(("MIT", url("http://github.com/scalatra/sbt-requirejs/raw/HEAD/LICENSE")))
+
+pomExtra <<= (pomExtra, name, description) {(pom, name, desc) => pom ++ Group(
+ <scm>
+ <connection>scm:git:git://github.com/scalatra/sbt-requirejs.git</connection>
+ <developerConnection>scm:git:git@github.com:scalatra/sbt-requirejs.git</developerConnection>
+ <url>https://github.com/scalatra/sbt-requirejs</url>
+ </scm>
+ <developers>
+ <developer>
+ <id>casualjim</id>
+ <name>Ivan Porto Carrero</name>
+ <url>http://flanders.co.nz/</url>
+ </developer>
+ </developers>
+)}
+
+
1 project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.11.3
5 project/plugins.sbt
@@ -0,0 +1,5 @@
+resolvers += Resolver.url("sbt-plugin-releases",
+ new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases/"))(
+ Resolver.ivyStylePatterns)
+
+ addSbtPlugin("com.jsuereth" % "xsbt-gpg-plugin" % "0.6")
15,563 src/main/resources/requirejs/r.js
15,563 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
137 src/main/scala/org/scalatra/requirejs/RequireJsPlugin.scala
@@ -0,0 +1,137 @@
+import sbt._
+import Keys._
+import net.liftweb.json._
+import JsonDSL._
+
+/*_*/
+
+/**
+ * A Plugin to use requirejs from sbt.
+ */
+object RequireJsPlugin extends Plugin {
+ object RequireJsKeys {
+ val optimize = TaskKey[Seq[File]]("optimize", "Compile and optimize script source files.")
+ val rjs = SettingKey[Option[File]]("rjs", "The r.js file to use for optimizing the source files.")
+ val createBuildProfile = TaskKey[File]("create-build-profile", "Generate the build profile to use when generating sources")
+ val requireJs = TaskKey[Seq[File]]("require-js", "Compile and optimize script source files and deploy to the webapp.")
+ val buildProfileFile = SettingKey[File]("build-profile-file", "The build profile base file for the require.js optimizer.")
+ val buildProfile = SettingKey[JValue]("build-profile", "The build profile for the require.js optimizer.")
+ val buildProfileGenerated = SettingKey[File]("build-profile-generated", "The generated build profile for the require.js optimizer")
+ val webApp = SettingKey[File]("webapp-dir", "The directory to copy the files to.")
+ val baseUrl = SettingKey[String]("base-url", "The base url for the require js script files")
+ val mainConfigFile = SettingKey[Option[File]]("main-config-file", "The main config file for require-js")
+ val nodeBin = SettingKey[String]("node-bin", "The location of the node binary")
+ }
+
+ import RequireJsKeys._
+
+ private def copyFromClassPathToFilesystem(classpathFilename: String, outputFile: File): File = {
+ Using.fileOutputStream()(outputFile) { output =>
+ IO.transferAndClose(getClass.getResourceAsStream(classpathFilename), output)
+ }
+ outputFile
+ }
+ private def unpackRjsFromResources(target: File) =
+ copyFromClassPathToFilesystem("/requirejs/r.js", target / "r.js").getAbsolutePath
+
+ private def optimizeTask =
+ (target in requireJs,
+ rjs in requireJs,
+ nodeBin in requireJs,
+ includeFilter in requireJs,
+ excludeFilter in requireJs,
+ createBuildProfile in requireJs, streams) map { (tgt, rjsf, node, incl, excl, bp, log) =>
+ val tool = rjsf.filter(_.exists).map(_.getAbsolutePath).getOrElse(unpackRjsFromResources(tgt))
+ val t = tgt.getAbsoluteFile
+ if (!t.exists()) IO.createDirectory(t.getAbsoluteFile)
+ val cmd = node + " " + tool + " -o " + bp.getAbsolutePath
+ cmd ! log.log
+ tgt.descendentsExcept(incl, excl).get
+ }
+
+ private def cleanBuildCache(buildCache: File, fallback: Seq[File], webapp: File) {
+ if (!buildCache.exists()) {
+ IO.delete(fallback filter (_.exists()))
+ } else {
+ IO.delete(IO.readLines(buildCache) filterNot (_ == webapp.getAbsolutePath) map file)
+ }
+ }
+
+ private def copyToWebApp =
+ (optimize in requireJs, target in requireJs, webApp in requireJs) map { (files, tgt, webapp) =>
+ val buildCache = (tgt / ".." / "requirejs.files.txt").getAbsoluteFile
+ val toCopy = files x rebase(tgt.getAbsoluteFile, webapp.getAbsoluteFile)
+ val copiedFiles = toCopy.map(_._2.getAbsoluteFile).filterNot(_.getPath == webapp.getAbsolutePath)
+ cleanBuildCache(buildCache, copiedFiles, webapp)
+ copyAndCacheResult(toCopy, buildCache, webapp.getAbsoluteFile)
+ copiedFiles
+ }
+
+
+ def copyAndCacheResult(toCopy: scala.Seq[(File, File)], buildCache: File, webapp: File) {
+ IO.copy(toCopy, overwrite = true)
+ IO.writeLines(buildCache, toCopy.map(_._2.getAbsolutePath).filterNot(_ == webapp.getAbsolutePath))
+ }
+
+ def generateBuildProfile =
+ (buildProfileFile in requireJs,
+ buildProfile in requireJs,
+ buildProfileGenerated in requireJs,
+ baseUrl in requireJs,
+ mainConfigFile in requireJs,
+ sourceDirectory in requireJs,
+ target in requireJs,
+ streams) map { (bpf, bp, gen, bd, mc, src, tgt, s) =>
+ val txt = if (bpf.exists) IO.read(bpf) else ""
+ val n = if (txt.startsWith("(")) txt.substring(1) else txt
+ val b = if (n.trim().endsWith(")")) n.substring(0, n.length - 1) else n
+ val fileJson = if (bpf.exists) Some(parse(b)) else None
+ val merged = fileJson map (_ merge bp) getOrElse bp
+ val json = merged merge (
+ ("appDir" -> src.getAbsolutePath) ~
+ ("baseUrl" -> bd) ~
+ ("dir" -> IO.relativize(gen.getParentFile, tgt.getAbsoluteFile).getOrElse("requirejs"))
+ )
+ val oj = mc map (m => json merge (("mainConfigFile" -> m.getAbsolutePath): JValue)) getOrElse json
+ IO.write(gen, "(%s)\n" format pretty(render(oj)), append = false)
+ gen
+ }
+
+ private def requireJsSettingsIn(c: Configuration): Seq[Setting[_]] =
+ inConfig(c)(requireJsSettings0 ++ Seq(
+ webApp in requireJs <<= (sourceDirectory in c)(_ / "webapp"),
+ sourceDirectory in requireJs <<= (sourceDirectory in c)(_ / "requirejs"),
+ rjs in requireJs := None,
+ nodeBin in requireJs := ("which node" !!).trim,
+ buildProfileFile in requireJs <<= (baseDirectory in c)(_ / "project" / "requirejs.build.js"),
+ buildProfile in requireJs := JNothing,
+ buildProfileGenerated in requireJs <<= (target in c)(_ / "requirejs.build.js"),
+ target in requireJs <<= (target in c)(_ / "requirejs"),
+ baseUrl in requireJs := "scripts",
+ mainConfigFile in requireJs := None,
+ includeFilter in requireJs := "*",
+ excludeFilter in requireJs := "build.txt" || (".*" - ".") || "_*" || HiddenFileFilter,
+ watchSources in requireJs <<= (unmanagedSources in requireJs)
+ )) ++ Seq(
+ watchSources <++= (unmanagedSources in requireJs in c),
+ compile in c <<= (compile in c).dependsOn(requireJs in c)
+ )
+
+ private def cleanTask =
+ (target in requireJs, webApp in requireJs, sourceDirectory in requireJs, unmanagedSources in requireJs) map { (tgt, webapp, src, files) =>
+ val buildCache = (tgt / ".." / "requirejs.files.txt").getAbsoluteFile
+ val toDelete = files x rebase(src.getAbsoluteFile, webapp.getAbsoluteFile)
+ cleanBuildCache(buildCache, toDelete.map(_._2.getAbsoluteFile), webapp)
+ }
+
+ def requireJsSettings: Seq[Setting[_]] = requireJsSettingsIn(Compile) ++ requireJsSettingsIn(Test)
+
+ private def requireJsSettings0: Seq[Setting[_]] = Seq(
+ unmanagedSources in requireJs <<= (sourceDirectory in requireJs, streams) map { (dir, _) => dir.get },
+ createBuildProfile in requireJs <<= generateBuildProfile,
+ optimize in requireJs <<= optimizeTask,
+ clean in requireJs <<= cleanTask,
+ requireJs <<= copyToWebApp
+ )
+}
+/*_*/

0 comments on commit 34a2a27

Please sign in to comment.