summaryrefslogtreecommitdiff
path: root/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala')
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala130
1 files changed, 130 insertions, 0 deletions
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala
new file mode 100644
index 0000000..24491b4
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala
@@ -0,0 +1,130 @@
+package scala.scalajs.tools.jsdep
+
+import scala.scalajs.tools.json._
+import scala.scalajs.tools.io._
+
+import scala.collection.immutable.{Seq, Traversable}
+
+import java.io.{Reader, Writer}
+
+/** The information written to a "JS_DEPENDENCIES" manifest file. */
+final class JSDependencyManifest(
+ val origin: Origin,
+ val libDeps: List[JSDependency],
+ val requiresDOM: Boolean,
+ val compliantSemantics: List[String]) {
+ def flatten: List[FlatJSDependency] = libDeps.map(_.withOrigin(origin))
+}
+
+object JSDependencyManifest {
+
+ final val ManifestFileName = "JS_DEPENDENCIES"
+
+ def createIncludeList(
+ flatDeps: Traversable[FlatJSDependency]): List[ResolutionInfo] = {
+ val jsDeps = mergeManifests(flatDeps)
+
+ // Verify all dependencies are met
+ for {
+ lib <- flatDeps
+ dep <- lib.dependencies
+ if !jsDeps.contains(dep)
+ } throw new MissingDependencyException(lib, dep)
+
+ // Sort according to dependencies and return
+
+ // Very simple O(n²) topological sort for elements assumed to be distinct
+ // Copied :( from GenJSExports (but different exception)
+ @scala.annotation.tailrec
+ def loop(coll: List[ResolutionInfo],
+ acc: List[ResolutionInfo]): List[ResolutionInfo] = {
+
+ if (coll.isEmpty) acc
+ else if (coll.tail.isEmpty) coll.head :: acc
+ else {
+ val (selected, pending) = coll.partition { x =>
+ coll forall { y => (x eq y) || !y.dependencies.contains(x.resourceName) }
+ }
+
+ if (selected.nonEmpty)
+ loop(pending, selected ::: acc)
+ else
+ throw new CyclicDependencyException(pending)
+ }
+ }
+
+ loop(jsDeps.values.toList, Nil)
+ }
+
+ /** Merges multiple JSDependencyManifests into a map of map:
+ * resourceName -> ResolutionInfo
+ */
+ private def mergeManifests(flatDeps: Traversable[FlatJSDependency]) = {
+ @inline
+ def hasConflict(x: FlatJSDependency, y: FlatJSDependency) = (
+ x.commonJSName.isDefined &&
+ y.commonJSName.isDefined &&
+ (x.resourceName == y.resourceName ^
+ x.commonJSName == y.commonJSName)
+ )
+
+ val conflicts = flatDeps.filter(x =>
+ flatDeps.exists(y => hasConflict(x,y)))
+
+ if (conflicts.nonEmpty)
+ throw new ConflictingNameException(conflicts.toList)
+
+ flatDeps.groupBy(_.resourceName).mapValues { sameName =>
+ new ResolutionInfo(
+ resourceName = sameName.head.resourceName,
+ dependencies = sameName.flatMap(_.dependencies).toSet,
+ origins = sameName.map(_.origin).toList,
+ commonJSName = sameName.flatMap(_.commonJSName).headOption
+ )
+ }
+ }
+
+ implicit object JSDepManJSONSerializer extends JSONSerializer[JSDependencyManifest] {
+ @inline def optList[T](x: List[T]): Option[List[T]] =
+ if (x.nonEmpty) Some(x) else None
+
+ def serialize(x: JSDependencyManifest): JSON = {
+ new JSONObjBuilder()
+ .fld("origin", x.origin)
+ .opt("libDeps", optList(x.libDeps))
+ .opt("requiresDOM", if (x.requiresDOM) Some(true) else None)
+ .opt("compliantSemantics", optList(x.compliantSemantics))
+ .toJSON
+ }
+ }
+
+ implicit object JSDepManJSONDeserializer extends JSONDeserializer[JSDependencyManifest] {
+ def deserialize(x: JSON): JSDependencyManifest = {
+ val obj = new JSONObjExtractor(x)
+ new JSDependencyManifest(
+ obj.fld[Origin] ("origin"),
+ obj.opt[List[JSDependency]]("libDeps").getOrElse(Nil),
+ obj.opt[Boolean] ("requiresDOM").getOrElse(false),
+ obj.opt[List[String]] ("compliantSemantics").getOrElse(Nil))
+ }
+ }
+
+ def write(dep: JSDependencyManifest, output: WritableVirtualTextFile): Unit = {
+ val writer = output.contentWriter
+ try write(dep, writer)
+ finally writer.close()
+ }
+
+ def write(dep: JSDependencyManifest, writer: Writer): Unit =
+ writeJSON(dep.toJSON, writer)
+
+ def read(file: VirtualTextFile): JSDependencyManifest = {
+ val reader = file.reader
+ try read(reader)
+ finally reader.close()
+ }
+
+ def read(reader: Reader): JSDependencyManifest =
+ fromJSON[JSDependencyManifest](readJSON(reader))
+
+}