diff options
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.scala | 130 |
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)) + +} |