Skip to content

Import the jsDependencies plugin from the Scala.js core repository. #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 68 commits into from
Jun 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
dbd1769
Add JS dependency tracking via manifests.
gzm0 Apr 30, 2014
8ca6c9a
Fix scala-js/scala-js#595: Test SBT plugin in published form on Travis
gzm0 May 7, 2014
1b4b0c7
Fix scala-js/scala-js#610: Restructure classpaths for clean linking API
gzm0 May 16, 2014
c2ac651
Fix scala-js/scala-js#750: Add a filter for jsDependencies
gzm0 Jun 25, 2014
bc00dd4
Make clean exceptions for tools
gzm0 Jul 27, 2014
3ac71c5
Fix scala-js/scala-js#706: Add mechanism to support CommonJS style re…
gzm0 Jul 27, 2014
0d756f4
Fix scala-js/scala-js#787: Add mechanism for propagation of DOM depen…
gzm0 Aug 7, 2014
b601e61
Refactor tools.json to abstract backend-specifics into a single file
gzm0 Jul 22, 2014
1936c76
Cross-build Scala.js tools
gzm0 Jul 11, 2014
ecbec49
Upgrade default Scala version to 2.11.2
gzm0 Nov 4, 2014
b0fcffb
Clean up standalone sbt test project
gzm0 Nov 4, 2014
6305bd3
Make PhantomJSEnv a ComJSEnv
gzm0 Nov 4, 2014
9cc08fb
Cleanup dependency data structures
gzm0 Nov 5, 2014
e1191c2
Fix scala-js/scala-js#1244: Track and verify required semantic compli…
gzm0 Nov 6, 2014
e0599b3
Fix scala-js/scala-js#1025: Turn the sbt plugin into an AutoPlugin.
sjrd Nov 24, 2014
32bbabd
Add factories to sbt plugin for PhantomJSEnv and NodeJSEnv
gzm0 Nov 25, 2014
27f4d18
Fix scala-js/scala-js#1308: Auto import RuntimeDOM builder
gzm0 Nov 25, 2014
4487470
Fix scala-js/scala-js#1073: New test framework interface (aligned wit…
gzm0 Nov 20, 2014
909973b
Merge pull request scala-js/scala-js#1289 from gzm0/test-interface
sjrd Nov 27, 2014
7e51005
Fix scala-js/scala-js#1287: Reorganize package names and artifacts
gzm0 Nov 27, 2014
91f481f
Fix scala-js/scala-js#1331: Repair %%% the macro (using a hack)
gzm0 Dec 1, 2014
ef3d3eb
Fix scala-js/scala-js#1067: Add a simple CrossProject mechanism
gzm0 Dec 1, 2014
bf85487
Fix scala-js/scala-js#1408: packageJSDependencies is broken.
sjrd Dec 24, 2014
71fd625
Upgrade to Scala 2.11.5.
sjrd Jan 8, 2015
6585c65
Fix scala-js/scala-js#1491: Give proper equals/hashCode to JSDependen…
sjrd Feb 25, 2015
f8efaf7
Fix scala-js/scala-js#1496: Allow partial paths in jsDependencies.
sjrd Feb 26, 2015
d3d56eb
Upgrade to Scala 2.10.5 and 2.11.6.
sjrd Mar 5, 2015
22e185b
Fix scala-js/scala-js#1571: Make FrameworkDetector resilient to other…
fomkin Mar 26, 2015
31ea780
Fix scala-js/scala-js#1569: Allow crossProject's JVM and JS parts to …
fomkin Mar 26, 2015
c61fb81
Merge pull request scala-js/scala-js#1570 from yelbota/fix-1569
sjrd Mar 31, 2015
aa0062d
Fix scala-js/scala-js#1564: Allow filtering of JS dependency manifests
gzm0 Apr 16, 2015
52cc81b
Merge pull request scala-js/scala-js#1605 from gzm0/manifest-filter
sjrd Apr 23, 2015
299da4b
Fix scala-js/scala-js#1612: Don't throw in ConflictingNameException ctor
gzm0 Apr 25, 2015
8e2377e
Clarify CommonJSName resolution error message
gzm0 Apr 24, 2015
860e28c
Fix scala-js/scala-js#1621: Don't skip packaging JS dependencies by d…
gzm0 Apr 29, 2015
8bffbfe
Fix scala-js/scala-js#1592: Support for minified JS dependencies
gzm0 Apr 23, 2015
a658715
Fix scala-js/scala-js#1752: Build for and upgrade to Scala 2.11.7.
sjrd Jun 25, 2015
5885556
Fix scala-js/scala-js#1844: Add JSDependency.toString method.
nicolasstucki Aug 20, 2015
cc47179
Fix scala-js/scala-js#1845: Add scalaJSOutputWrapper sbt setting.
nicolasstucki Aug 26, 2015
1640f6f
Fix scala-js/scala-js#1690: Add JUnit framework and plugin.
nicolasstucki Oct 7, 2015
949bc34
Fix scala-js/scala-js#1949: Add/fix toString() methods for tools.jsde…
sjrd Jan 9, 2016
1ddf909
Tools refactoring
gzm0 Jan 17, 2016
8b5fad3
Fix scala-js/scala-js#2172: Remove deprecated stuff in tools and js-e…
sjrd Jan 17, 2016
159c131
Make private everything that can be in tools and js-envs.
sjrd Jan 17, 2016
8e52f6c
Fix scala-js/scala-js#2202: Prevent concurrent use of linker in sbt
gzm0 Jan 26, 2016
667c417
Fix scala-js/scala-js#2198: Allow non-existent classpath entries
gzm0 Jan 26, 2016
62e41b6
Merge pull request scala-js/scala-js#2201 from gzm0/empty-classpath-e…
sjrd Jan 27, 2016
1d10fdb
Fix scala-js/scala-js#2005: Add cross-version shared source directories.
sjrd Feb 6, 2016
79fe6cb
Fix scala-js/scala-js#2243: fix JSDependencies cache path.
nicolasstucki Mar 9, 2016
f6e27ab
Fix scala-js/scala-js#2284: Upgrade to 2.11.8.
nicolasstucki Mar 14, 2016
4f3f9db
Fix scala-js/scala-js#2350: Add an sbt setting isScalaJSProject.
sjrd Apr 24, 2016
635250a
Remove Jasmine from sbtPluginTest.
nicolasstucki Apr 26, 2016
4ef78d6
Fix JUnit Scala decoded names.
nicolasstucki Apr 27, 2016
985adc2
Move sbt-plugin code to right path {scala => org}
gzm0 May 30, 2016
68a8829
Fix scala-js/scala-js#2494: Store the location of source map files as…
sjrd Jul 6, 2016
80da1f5
Test ManifestFilters
gzm0 Aug 19, 2016
97867d3
Test ComplianceRequirement
gzm0 Aug 20, 2016
7c4ffc0
Separate all PhantomJS-related things in distinct projects.
sjrd Mar 27, 2017
40382fe
Do not use jsDependencies (nor jQuery) in sbt-plugin-test/withDOM.
sjrd Apr 5, 2017
eb8adf8
Fix scala-js/scala-js#2883: Remove packageJSLauncher
gzm0 Apr 9, 2017
baa90ee
Restore the test for scala-js/scala-js#2202 (concurrent use of the li…
sjrd Apr 13, 2017
7003e11
Upgrade to Scala 2.11.11.
sjrd Apr 24, 2017
ee176f8
Merge '0.6.x' into 'master'.
sjrd Apr 28, 2017
ea89fff
Fix scala-js/scala-js#2839: Remove PhantomJS-related stuff in the cor…
sjrd May 21, 2017
2cc41e1
Remove the `checkScalaJSSemantics` feature.
sjrd May 12, 2017
72ee239
Separate all jsDependencies-related things in separate projects.
sjrd May 12, 2017
f4ee044
Merge '0.6.x' into 'master'.
sjrd May 29, 2017
14457da
Import the jsDependencies plugin from the Scala.js core repository.
sjrd Jun 1, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
sudo: false
language: scala
scala:
- 2.10.6
- 2.11.11
- 2.12.2
jdk:
- oraclejdk8
install:
- if [[ "${TRAVIS_SCALA_VERSION}" == "2.10.6" ]]; then TEST_SBT_PLUGIN=true; else TEST_SBT_PLUGIN=false; fi
# The default ivy resolution takes way too much time, and times out Travis builds.
# We use coursier instead.
- mkdir -p $HOME/.sbt/0.13/plugins/
- echo 'addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-RC3")' > $HOME/.sbt/0.13/plugins/coursier.sbt
# While there is no published version of Scala.js 1.x, we have to locally build a snapshot.
- git clone https://github.com/scala-js/scala-js.git
- cd scala-js
- git checkout cc6610bf35332b5a52276b8fda69dee5ea72300f
- sbt ++$TRAVIS_SCALA_VERSION ir/publishLocal tools/publishLocal
- |
if [[ "${TEST_SBT_PLUGIN}" == "true" ]]; then
sbt ++$TRAVIS_SCALA_VERSION jsEnvs/publishLocal testAdapter/publishLocal sbtPlugin/publishLocal &&
sbt ++2.11.11 compiler/publishLocal library/publishLocal testInterface/publishLocal
fi
- cd ..
script:
- sbt ++$TRAVIS_SCALA_VERSION jsdependencies-core/test jsdependencies-core/doc
- |
if [[ "${TEST_SBT_PLUGIN}" == "true" ]]; then
sbt jsdependencies-core/publishLocal sbt-jsdependencies/publishLocal && \
cd sbt-plugin-test && \
sbt testAll && \
cd ..
fi
cache:
directories:
- $HOME/.ivy2/cache
- $HOME/.sbt
- $HOME/.coursier/cache
before_cache:
- find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete
- find $HOME/.sbt -name "*.lock" -print -delete
- rm $HOME/.sbt/0.13/plugins/coursier.sbt
104 changes: 104 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
val scalaJSVersion = "1.0.0-SNAPSHOT"

inThisBuild(Seq(
version := "0.1.0-SNAPSHOT",
organization := "org.scala-js",

crossScalaVersions := Seq("2.10.6", "2.11.11", "2.12.2"),
scalaVersion := "2.10.6",
scalacOptions ++= Seq("-deprecation", "-feature", "-Xfatal-warnings"),

homepage := Some(url("https://www.scala-js.org/")),
licenses += ("BSD New",
url("https://github.com/scala-js/jsdependencies/blob/master/LICENSE")),
scmInfo := Some(ScmInfo(
url("https://github.com/scala-js/jsdependencies"),
"scm:git:git@github.com:scala-js/jsdependencies.git",
Some("scm:git:git@github.com:scala-js/jsdependencies.git")))
))

val commonSettings = Def.settings(
// Scaladoc linking
apiURL := {
val name = moduleName.value
val v = version.value
Some(url(s"https://www.scala-js.org/api/$name/$v/"))
},
autoAPIMappings := true,

publishMavenStyle := true,
publishTo := {
val nexus = "https://oss.sonatype.org/"
if (isSnapshot.value)
Some("snapshots" at nexus + "content/repositories/snapshots")
else
Some("releases" at nexus + "service/local/staging/deploy/maven2")
},
pomExtra := (
<developers>
<developer>
<id>sjrd</id>
<name>Sébastien Doeraene</name>
<url>https://github.com/sjrd/</url>
</developer>
<developer>
<id>gzm0</id>
<name>Tobias Schlatter</name>
<url>https://github.com/gzm0/</url>
</developer>
<developer>
<id>nicolasstucki</id>
<name>Nicolas Stucki</name>
<url>https://github.com/nicolasstucki/</url>
</developer>
</developers>
),
pomIncludeRepository := { _ => false }
)

lazy val root: Project = project.in(file(".")).
settings(
publishArtifact in Compile := false,
publish := {},
publishLocal := {},

clean := clean.dependsOn(
clean in `jsdependencies-core`,
clean in `sbt-jsdependencies`
).value
)

lazy val `jsdependencies-core`: Project = project.in(file("jsdependencies-core")).
settings(
commonSettings,

libraryDependencies ++= Seq(
"org.scala-js" %% "scalajs-tools" % scalaJSVersion,

"com.novocode" % "junit-interface" % "0.11" % "test"
)
)

lazy val `sbt-jsdependencies`: Project = project.in(file("jsdependencies-sbt-plugin")).
settings(
commonSettings,

sbtPlugin := true,
scalaBinaryVersion :=
CrossVersion.binaryScalaVersion(scalaVersion.value),

addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion),

// Add API mappings for sbt (seems they don't export their API URL)
apiMappings ++= {
val deps = (externalDependencyClasspath in Compile).value
val sbtJars = deps filter { attributed =>
val p = attributed.data.getPath
p.contains("/org.scala-sbt/") && p.endsWith(".jar")
}
val docUrl =
url(s"http://www.scala-sbt.org/${sbtVersion.value}/api/")
sbtJars.map(_.data -> docUrl).toMap
}
).
dependsOn(`jsdependencies-core`)
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package org.scalajs.jsdependencies.core

import scala.collection.mutable

import org.scalajs.core.tools.io.VirtualJSFile
import JSLibResolveException.Problem

object DependencyResolver {

type DependencyFilter =
Traversable[FlatJSDependency] => Traversable[FlatJSDependency]

/** Constructs an ordered list of JS libraries to include. Fails if:
* - Resource names do not identify a unique resource on the classpath
* - Dependencies have cycles
* - Not all dependencies are available
*/
def resolveDependencies(
manifests: Traversable[JSDependencyManifest],
availableLibs: Map[String, VirtualJSFile],
dependencyFilter: DependencyFilter): List[ResolvedJSDependency] = {

val resourceNames = collectAllResourceNames(manifests)
val resolvedJSLibs =
resolveAllResourceNames(resourceNames, availableLibs.keys)

val allFlatDeps = for {
manifest <- manifests
dep <- manifest.libDeps
} yield {
new FlatJSDependency(
manifest.origin,
resolvedJSLibs(dep.resourceName),
dep.dependencies.map(resolvedJSLibs),
dep.commonJSName,
dep.minifiedResourceName.map(resolvedJSLibs))
}

val flatDeps = dependencyFilter(allFlatDeps)
val includeList = createIncludeList(flatDeps)

for (info <- includeList) yield {
new ResolvedJSDependency(availableLibs(info.relPath),
info.relPathMinified.map(availableLibs), info)
}
}

/** Collects all the resource names mentioned in the manifests.
* @param manifests to collect from
* @return Map from resource name to the list of origins mentioning them
*/
private def collectAllResourceNames(
manifests: Traversable[JSDependencyManifest]): Map[String, List[Origin]] = {

def allResources(dep: JSDependency) =
dep.resourceName :: dep.dependencies ::: dep.minifiedResourceName.toList

val nameOriginPairs = for {
manifest <- manifests.toList
dep <- manifest.libDeps
resourceName <- allResources(dep)
} yield (resourceName, manifest.origin)

nameOriginPairs.groupBy(_._1).mapValues(_.map(_._2))
}

/** Resolves all the resource names wrt to the current classpath.
* @throws JSLibResolveException if any of the resource names cannot be resolved
* @return Map from resource name to relative path
*/
private def resolveAllResourceNames(
allResourceNames: Map[String, List[Origin]],
relPaths: Traversable[String]): Map[String, String] = {
val problems = mutable.ListBuffer.empty[Problem]
val resolvedLibs = Map.newBuilder[String, String]

for ((resourceName, origins) <- allResourceNames) {
resolveResourceName(resourceName, origins, relPaths).fold[Unit](
problems += _,
resolvedLibs += resourceName -> _)
}

if (problems.nonEmpty)
throw new JSLibResolveException(problems.toList)
else
resolvedLibs.result()
}

/** Resolves one resource name wrt to the current classpath. */
private def resolveResourceName(resourceName: String, origins: List[Origin],
relPaths: Traversable[String]): Either[Problem, String] = {
val candidates = (relPaths collect {
case relPath if ("/" + relPath).endsWith("/" + resourceName) =>
relPath
}).toList

candidates match {
case relPath :: Nil =>
Right(relPath)
case _ =>
Left(new Problem(resourceName, candidates, origins))
}
}

/** Create a sorted include list for js libs */
private 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.relPath) }
}

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]) = {
checkCommonJSNameConflicts(flatDeps)

val byRelPath = flatDeps.groupBy(_.relPath)

checkMinifiedJSConflicts(byRelPath)

byRelPath.mapValues { sameName =>
new ResolutionInfo(
relPath = sameName.head.relPath,
dependencies = sameName.flatMap(_.dependencies).toSet,
origins = sameName.map(_.origin).toList,
commonJSName = sameName.flatMap(_.commonJSName).headOption,
relPathMinified = sameName.flatMap(_.relPathMinified).headOption
)
}
}

private def checkCommonJSNameConflicts(flatDeps: Traversable[FlatJSDependency]) = {
@inline
def hasConflict(x: FlatJSDependency, y: FlatJSDependency) = (
x.commonJSName.isDefined &&
y.commonJSName.isDefined &&
(x.relPath == y.relPath ^ x.commonJSName == y.commonJSName)
)

val conflicts = for {
dep <- flatDeps
if flatDeps.exists(hasConflict(dep, _))
} yield dep

if (conflicts.nonEmpty)
throw new ConflictingNameException(conflicts.toList)
}

private def checkMinifiedJSConflicts(
byRelPath: Map[String, Traversable[FlatJSDependency]]) = {

@inline
def hasConflict(x: FlatJSDependency, y: FlatJSDependency) = (
x.relPathMinified.isDefined &&
y.relPathMinified.isDefined &&
x.relPathMinified != y.relPathMinified
)

val conflicts = for {
(_, deps) <- byRelPath
x <- deps if deps.exists(y => hasConflict(x, y))
} yield x

if (conflicts.nonEmpty)
throw new ConflictingMinifiedJSException(conflicts.toList)
}

}
Loading