{"action":"created","comment":{"url":"https://api.github.com/repos/scala-js/scala-js/pulls/comments/42141111","id":42141111,"diff_hunk":"@@ -0,0 +1,447 @@\n+package org.scalajs.junit.plugin\n+\n+import scala.language.reflectiveCalls\n+\n+import org.scalajs.junit._\n+\n+import scala.reflect.internal.Flags\n+import scala.tools.nsc._\n+import scala.tools.nsc.plugins.{\n+Plugin => NscPlugin, PluginComponent => NscPluginComponent\n+}\n+\n+class ScalaJSJUnitPlugin(val global: Global) extends NscPlugin {\n+  selfPlugin =>\n+\n+  val name: String = \"Scala.js JUnit plugin\"\n+\n+  val components: List[NscPluginComponent] =\n+    List(ScalaJSJUnitPluginComponent)\n+\n+  val description: String = \"Adapts JUnit test classes to Scala.js\"\n+\n+  object ScalaJSJUnitPluginComponent extends plugins.PluginComponent\n+      with transform.Transform {\n+\n+    protected def newTransformer(\n+        unit: global.CompilationUnit): global.Transformer = {\n+      new ScalaJSJUnitPluginTransformer\n+    }\n+\n+    val global: Global = selfPlugin.global\n+    val phaseName: String = \"junit-inject\"\n+    val runsAfter: List[String] = List(\"mixin\")\n+    override val runsBefore: List[String] = List(\"jscode\")\n+\n+    class ScalaJSJUnitPluginTransformer extends global.Transformer {\n+\n+      import global._\n+\n+      type ScalaJSPlugin = NscPlugin {\n+        def PrepInteropComponent: AnyRef {\n+          def genExportMember(sym: Symbol): List[Tree]\n+          def registerModuleExports(sym: Symbol): Unit\n+          def genNamedExport(defSym: Symbol, jsName: String, pos: Position): Unit\n+        }\n+      }\n+\n+      lazy val scalaJSPlugin = {\n+        global.plugins.collectFirst {\n+          case pl if pl.getClass.getName == \"org.scalajs.core.compiler.ScalaJSPlugin\" => pl\n+        }.get.asInstanceOf[ScalaJSPlugin]\n+      }\n+\n+      override def transform(tree: Tree): Tree = tree match {\n+        case tree: PackageDef =>\n+          def classHasJUnitAnnotations(clazz: ClassDef): Boolean = {\n+            clazz.impl.body.exists {\n+              case ddef: DefDef => hasAnnotation[org.junit.Test](ddef)\n+              case _ => false\n+            }\n+          }\n+          val newStats = tree.stats.groupBy {\n+            case clDef: ClassDef => clDef.name\n+            case _ => \"not a class def\"\n+          }.iterator.flatMap {\n+            case (\"not a class def\", xs) => xs\n+            case (className, x :: xs) =>\n+              val clDef1 = x.asInstanceOf[ClassDef]\n+              val clDef2Option = xs match {\n+                case Nil => None\n+                case y :: ys =>\n+                  if (ys.isEmpty) Some(y.asInstanceOf[ClassDef])\n+                  else None\n+              }\n+              val (clDefOption, mlDefOption) = {\n+                if (clDef1.mods.hasFlag(Flags.MODULE))\n+                  (clDef2Option, Some(clDef1))\n+                else (Some(clDef1), clDef2Option)\n+              }\n+\n+              if ((clDefOption.isDefined && classHasJUnitAnnotations(clDefOption.get)) ||\n+                (mlDefOption.isDefined && classHasJUnitAnnotations(mlDefOption.get))) {\n+                val transformedClDef = clDefOption match {\n+                  case Some(clDef) => transformTestClass(clDef)\n+\n+                  case None =>\n+                    throw new ClassNotFoundException(\n+                      mlDefOption.get.name.toString)\n+                }\n+                val hookClass = mkHookClass(transformedClDef, mlDefOption)\n+                hookClass :: transformedClDef :: mlDefOption.toList\n+              }\n+              else x :: xs\n+\n+            case (_, Nil) => Nil\n+          }\n+\n+          val newPackage = tree.copy(stats = newStats.toList)\n+          newPackage.setSymbol(tree.symbol)\n+          newPackage\n+\n+        case _ =>\n+          super.transform(tree)\n+      }\n+\n+      def getLocalMethodSymbol(clazz: ClassDef): Symbol = {\n+        clazz.impl.body.collectFirst {\n+          case dDef: DefDef if hasAnnotation[org.junit.Test](dDef) => dDef\n+        }.get.symbol\n+      }\n+\n+      def transformTestClass(clazz: ClassDef): ClassDef = {\n+        inform(\"Scala.js JUnit plugin: transforming \" + clazz.name)\n+        val invokeJUnitMethodDef = {\n+          val annotatedMethods = jUnitAnnotatedMethods(clazz)\n+          val localMethodSymbol = getLocalMethodSymbol(clazz)\n+          mkInvokeJUnitMethodDef(annotatedMethods, localMethodSymbol)\n+        }\n+\n+        val newBody = invokeJUnitMethodDef :: clazz.impl.body\n+        val newParents = clazz.impl.parents :::\n+          TypeTree(typeOf[ScalaJSJUnitTest]) :: Nil\n+        val newImpl = clazz.impl.copy(parents = newParents, body = newBody)\n+\n+        val newClazz =\n+          gen_mkClassDef_compat(Modifiers(), clazz.name, Nil, newImpl)\n+        val clazzSym = {\n+          val clazzSym = clazz.symbol\n+          val newClazzInfo = {\n+            val newParentsInfo = clazzSym.info.parents :::\n+              typeOf[ScalaJSJUnitTest] :: Nil\n+            val decls = clazzSym.info.decls\n+            decls.enter(invokeJUnitMethodDef.symbol)\n+            ClassInfoType(newParentsInfo, decls, clazzSym.info.typeSymbol)\n+          }\n+          clazzSym.setInfo(newClazzInfo)\n+          clazzSym\n+        }\n+        newClazz.setSymbol(clazzSym)\n+\n+        newClazz\n+      }\n+\n+      def mkHookClass(clazz: ClassDef, mlDefOption: Option[ClassDef]): Tree = {\n+        val clazzSym = clazz.symbol.cloneSymbol\n+        val getJUnitMetadataDef = mkGetJUnitMetadataDef(clazz, mlDefOption)\n+        val newInstanceDef = mkNewInstanceDef(clazz, mlDefOption)\n+        val invokeJUnitMethodDef = {\n+          val annotatedMethods = mlDefOption.fold[List[DefDef]](Nil)(jUnitAnnotatedMethods)\n+          val localMethodSymbol = (annotatedMethods ::: getMethods(clazz)).head.symbol\n+          localMethodSymbol.setInfo(localMethodSymbol.info.atOwner(clazzSym))\n+          mkInvokeJUnitMethodDef(annotatedMethods, localMethodSymbol)\n+        }\n+\n+        val newBody = getJUnitMetadataDef :: newInstanceDef :: invokeJUnitMethodDef :: Nil\n+\n+        val newParents = TypeTree(typeOf[java.lang.Object]) ::\n+          TypeTree(typeOf[JUnitTestMetadata]) :: Nil\n+\n+        val newImpl = clazz.impl.copy(parents = newParents, body = newBody)\n+\n+        val newClazz = {\n+          val newClassName = newTypeName(clazz.name.toString + \"$scalajs$junit$hook\")\n+          val newClazz = gen_mkClassDef_compat(Modifiers(Flags.MODULE),\n+            newClassName, Nil, newImpl)\n+          clazzSym.flags += Flags.MODULE\n+          clazzSym.withoutAnnotations\n+          clazzSym.setName(newClassName)\n+          val newClazzInfo = {\n+            val newParentsInfo = typeOf[java.lang.Object] ::\n+              typeOf[JUnitTestMetadata] :: Nil\n+            val decls = clazzSym.info.decls\n+            decls.enter(getJUnitMetadataDef.symbol)\n+            decls.enter(newInstanceDef.symbol)\n+            decls.enter(invokeJUnitMethodDef.symbol)\n+            ClassInfoType(newParentsInfo, decls, clazzSym.info.typeSymbol)\n+          }\n+          clazzSym.setInfo(newClazzInfo)\n+          scalaJSPlugin.PrepInteropComponent.registerModuleExports(clazzSym)\n+\n+          newClazz.setSymbol(clazzSym)\n+          newClazz\n+        }\n+        typer.typedClassDef(newClazz)\n+      }\n+\n+      def jUnitAnnotatedMethods(clDef: ClassDef): List[DefDef] = {\n+        clDef.impl.body.collect {\n+          case ddef: DefDef if hasJUnitMethodAnnotation(ddef) => ddef\n+        }\n+      }\n+\n+      def getMethods(clazzDef: ClassDef): List[DefDef] =\n+        clazzDef.impl.body.collect { case dDef: DefDef => dDef }\n+\n+      def mkInvokeJUnitMethodDef(methods: List[DefDef],\n+          localMethodSymbol: Symbol): DefDef = {\n+        val invokeJUnitMethodSym = localMethodSymbol.cloneSymbol\n+        invokeJUnitMethodSym.withoutAnnotations\n+        invokeJUnitMethodSym.setName(newTermName(\"scalajs$junit$invoke\"))\n+\n+        val paramSym =\n+          invokeJUnitMethodSym.newValueParameter(newTermName(\"methodId\"))\n+        paramSym.setInfo(typeOf[java.lang.String])\n+\n+        invokeJUnitMethodSym.setInfo(MethodType(List(paramSym), typeOf[Unit]))\n+\n+        val methodNotFound = {\n+          val msg = gen.mkMethodCall(paramSym, newTermName(\"$plus\"), Nil,\n+            List(Literal(Constant(\" not found\"))))\n+          val exceptionConstructor = Select(\n+            New(TypeTree(typeOf[NoSuchMethodException])),\n+            termNames_CONSTRUCTOR)\n+          val exception = Apply(exceptionConstructor, List(msg))\n+          Throw(exception)\n+        }\n+\n+        def mkInvokeJUnitMethodRhs(\n+            remainingTestMethods: List[(DefDef, Int)]): Tree = {\n+          remainingTestMethods match {\n+            case (method, id) :: tail =>\n+              val symbol = method.symbol\n+              val headMethodId = Literal(Constant(id.toString))\n+              If(\n+                gen.mkMethodCall(paramSym, newTermName(\"$eq$eq\"), Nil,\n+                  List(headMethodId)),\n+                gen.mkMethodCall(symbol, Nil),\n+                mkInvokeJUnitMethodRhs(tail))\n+            case Nil =>\n+              methodNotFound\n+          }\n+        }\n+        val invokeJUnitMethodRhs =\n+          atOwner(invokeJUnitMethodSym)(typer.typed(mkInvokeJUnitMethodRhs(\n+            methods.zipWithIndex)))\n+        val param = newValDef_compat(paramSym, EmptyTree)()\n+        typer.typedDefDef(newDefDef_compat(invokeJUnitMethodSym, invokeJUnitMethodRhs)(\n+          vparamss = List(List(param))))\n+      }\n+\n+      def mkGetJUnitMetadataDef(clDef: ClassDef,\n+          mlDefOption: Option[ClassDef]): DefDef = {\n+        val methods = jUnitAnnotatedMethods(clDef)\n+        val mlMethods = mlDefOption.map(jUnitAnnotatedMethods)\n+\n+        def mkNewInstance[T: TypeTag](params: List[Tree] = Nil): Tree =\n+          Apply(Select(New(TypeTree(typeOf[T])), termNames_CONSTRUCTOR), params)\n+\n+        def liftAnnotations(methodSymbol: Symbol): List[Tree] = {\n+          val annotations = methodSymbol.annotations\n+          def lift(t: Tree): Tree = {\n+            t match {\n+              case lit: Literal => lit\n+\n+              case Select(t2, name) => Select(lift(t2), name)\n+\n+              case ValDef(mod, name, tpt, rhs) =>\n+                ValDef(mod, name, tpt, lift(rhs))\n+\n+              case _ => t\n+            }\n+          }\n+          def liftList(params: List[Tree]): List[Tree] =\n+            params.map(lift)\n+\n+          annotations.collect {\n+            case ann if ann.atp == typeOf[org.junit.Test] =>\n+              ann.original match {\n+                case Block(stats, _) =>\n+                  // TODO: Add support for @Test(timeout=...)\n+                  // val newStats = stats\n+                  // val newArgs = liftList(ann.args)\n+                  // val newAnn = mkNewInstance[org.junit.Test](newArgs)\n+                  // Block(newStats, newAnn)\n+                  throw new UnsupportedOperationException(\n+                    \"@Test(timeout = ...) is not yet supported in \" +\n+                      \"Scala.js JUnit Framework\")\n+\n+                case _ => mkNewInstance[org.junit.Test](liftList(ann.args))\n+              }\n+\n+            case ann if ann.atp == typeOf[org.junit.Before] =>\n+              mkNewInstance[org.junit.Before]()\n+\n+            case ann if ann.atp == typeOf[org.junit.After] =>\n+              mkNewInstance[org.junit.After]()\n+\n+            case ann if ann.atp == typeOf[org.junit.BeforeClass] =>\n+              mkNewInstance[org.junit.BeforeClass]()\n+\n+            case ann if ann.atp == typeOf[org.junit.AfterClass] =>\n+              mkNewInstance[org.junit.AfterClass]()\n+\n+            case ann if ann.atp == typeOf[org.junit.Ignore] =>\n+              mkNewInstance[org.junit.Ignore](liftList(ann.args))\n+\n+            case ann if ann.atp == typeOf[org.junit.FixMethodOrder] =>\n+              // TODO: Add support for @FixMethodOrder(...)\n+              // mkNewInstance[org.junit.FixMethodOrder](liftList(ann.args))\n+              throw new UnsupportedOperationException(\n+                \"@FixMethodOrder(...) is not yet supported in \" +\n+                  \"Scala.js JUnit Framework\")\n+\n+          }\n+        }\n+\n+        def defaultMethodMetadata[T: TypeTag](methodAndId: (DefDef, Int)): Tree = {\n+          val (m, id) = methodAndId\n+          val annotations = liftAnnotations(m.symbol)\n+          Apply(\n+            Select(New(TypeTree(typeOf[T])), termNames_CONSTRUCTOR),\n+            List(\n+              Literal(Constant(m.name.toString)),\n+              Literal(Constant(id.toString)),\n+              mkList[java.lang.annotation.Annotation](annotations)\n+            ))\n+        }\n+\n+        def mkList[T: TypeTag](elems: List[Tree]): Tree = {\n+          val array = ArrayValue(TypeTree(typeOf[T]), elems)\n+          val castArray = gen.mkCast(array, typeOf[Array[Object]])\n+          val wrappedArray = gen.mkMethodCall(\n+            definitions.PredefModule,\n+            definitions.wrapArrayMethodName(typeOf[Object]),\n+            Nil, List(castArray))\n+          gen.mkMethodCall(definitions.List_apply, List(wrappedArray))\n+        }\n+\n+        def mkMethodList[T: TypeTag](testMethods: List[DefDef]): Tree =\n+          mkList[T](testMethods.zipWithIndex.map(defaultMethodMetadata[T]))\n+\n+        val getJUnitMethodRhs = {\n+          Apply(\n+            Select(\n+              New(TypeTree(typeOf[JUnitClassMetadata])),\n+              termNames_CONSTRUCTOR),\n+            List(\n+              mkList[java.lang.annotation.Annotation](\n+                      liftAnnotations(clDef.symbol)),\n+              gen.mkNil,\n+              mkMethodList[JUnitMethodMetadata](methods),\n+              mlMethods.\n+                map(mkMethodList[JUnitMethodMetadata]).getOrElse(gen.mkNil)","path":"junit-plugin/src/main/scala/org/scalajs/junit/plugin/ScalaJSJUnitPlugin.scala","position":343,"original_position":343,"commit_id":"cfdf844b0d82716f23a15b7be920f81fdf8917ae","original_commit_id":"cfdf844b0d82716f23a15b7be920f81fdf8917ae","user":{"login":"sjrd","id":535934,"avatar_url":"https://avatars.githubusercontent.com/u/535934?v=3","gravatar_id":"","url":"https://api.github.com/users/sjrd","html_url":"https://github.com/sjrd","followers_url":"https://api.github.com/users/sjrd/followers","following_url":"https://api.github.com/users/sjrd/following{/other_user}","gists_url":"https://api.github.com/users/sjrd/gists{/gist_id}","starred_url":"https://api.github.com/users/sjrd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sjrd/subscriptions","organizations_url":"https://api.github.com/users/sjrd/orgs","repos_url":"https://api.github.com/users/sjrd/repos","events_url":"https://api.github.com/users/sjrd/events{/privacy}","received_events_url":"https://api.github.com/users/sjrd/received_events","type":"User","site_admin":false},"body":"Break after `map(` if you must, but not after `.`","created_at":"2015-10-15T15:42:19Z","updated_at":"2015-10-15T15:42:19Z","html_url":"https://github.com/scala-js/scala-js/pull/1959#discussion_r42141111","pull_request_url":"https://api.github.com/repos/scala-js/scala-js/pulls/1959","_links":{"self":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/comments/42141111"},"html":{"href":"https://github.com/scala-js/scala-js/pull/1959#discussion_r42141111"},"pull_request":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/1959"}}},"pull_request":{"url":"https://api.github.com/repos/scala-js/scala-js/pulls/1959","id":47785184,"html_url":"https://github.com/scala-js/scala-js/pull/1959","diff_url":"https://github.com/scala-js/scala-js/pull/1959.diff","patch_url":"https://github.com/scala-js/scala-js/pull/1959.patch","issue_url":"https://api.github.com/repos/scala-js/scala-js/issues/1959","number":1959,"state":"open","locked":false,"title":"Fix #1690: Add JUnit framework and plugin.","user":{"login":"nicolasstucki","id":3648029,"avatar_url":"https://avatars.githubusercontent.com/u/3648029?v=3","gravatar_id":"","url":"https://api.github.com/users/nicolasstucki","html_url":"https://github.com/nicolasstucki","followers_url":"https://api.github.com/users/nicolasstucki/followers","following_url":"https://api.github.com/users/nicolasstucki/following{/other_user}","gists_url":"https://api.github.com/users/nicolasstucki/gists{/gist_id}","starred_url":"https://api.github.com/users/nicolasstucki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nicolasstucki/subscriptions","organizations_url":"https://api.github.com/users/nicolasstucki/orgs","repos_url":"https://api.github.com/users/nicolasstucki/repos","events_url":"https://api.github.com/users/nicolasstucki/events{/privacy}","received_events_url":"https://api.github.com/users/nicolasstucki/received_events","type":"User","site_admin":false},"body":"* Add junit-runntime:\r\n * Adapt JUnit classes\r\n * Adapt sbt junit-interface\r\n * Adapt hamcrest Matchers (partial support)\r\n * Custom sbt JUnit test framework.\r\n* Add junit-plugin: Transforms classes containing method with @Test\r\n  to retain data that would usually be accessed through reflection.\r\n* Create new sbt confuiguration for plugins that are only added in\r\n  test configurations ('scala-js-test-plugin').\r\n* Add JUnit framework to test-suite\r\n* Add JUnit plugin test in sbt-plugin-test","created_at":"2015-10-15T13:22:57Z","updated_at":"2015-10-15T15:42:19Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6a7e1338c745983fb8893ee96036ae7945f69fc5","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/scala-js/scala-js/pulls/1959/commits","review_comments_url":"https://api.github.com/repos/scala-js/scala-js/pulls/1959/comments","review_comment_url":"https://api.github.com/repos/scala-js/scala-js/pulls/comments{/number}","comments_url":"https://api.github.com/repos/scala-js/scala-js/issues/1959/comments","statuses_url":"https://api.github.com/repos/scala-js/scala-js/statuses/cfdf844b0d82716f23a15b7be920f81fdf8917ae","head":{"label":"nicolasstucki:junit-test","ref":"junit-test","sha":"cfdf844b0d82716f23a15b7be920f81fdf8917ae","user":{"login":"nicolasstucki","id":3648029,"avatar_url":"https://avatars.githubusercontent.com/u/3648029?v=3","gravatar_id":"","url":"https://api.github.com/users/nicolasstucki","html_url":"https://github.com/nicolasstucki","followers_url":"https://api.github.com/users/nicolasstucki/followers","following_url":"https://api.github.com/users/nicolasstucki/following{/other_user}","gists_url":"https://api.github.com/users/nicolasstucki/gists{/gist_id}","starred_url":"https://api.github.com/users/nicolasstucki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nicolasstucki/subscriptions","organizations_url":"https://api.github.com/users/nicolasstucki/orgs","repos_url":"https://api.github.com/users/nicolasstucki/repos","events_url":"https://api.github.com/users/nicolasstucki/events{/privacy}","received_events_url":"https://api.github.com/users/nicolasstucki/received_events","type":"User","site_admin":false},"repo":{"id":35167756,"name":"scala-js","full_name":"nicolasstucki/scala-js","owner":{"login":"nicolasstucki","id":3648029,"avatar_url":"https://avatars.githubusercontent.com/u/3648029?v=3","gravatar_id":"","url":"https://api.github.com/users/nicolasstucki","html_url":"https://github.com/nicolasstucki","followers_url":"https://api.github.com/users/nicolasstucki/followers","following_url":"https://api.github.com/users/nicolasstucki/following{/other_user}","gists_url":"https://api.github.com/users/nicolasstucki/gists{/gist_id}","starred_url":"https://api.github.com/users/nicolasstucki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nicolasstucki/subscriptions","organizations_url":"https://api.github.com/users/nicolasstucki/orgs","repos_url":"https://api.github.com/users/nicolasstucki/repos","events_url":"https://api.github.com/users/nicolasstucki/events{/privacy}","received_events_url":"https://api.github.com/users/nicolasstucki/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/nicolasstucki/scala-js","description":"Scala.js, the Scala to JavaScript compiler","fork":true,"url":"https://api.github.com/repos/nicolasstucki/scala-js","forks_url":"https://api.github.com/repos/nicolasstucki/scala-js/forks","keys_url":"https://api.github.com/repos/nicolasstucki/scala-js/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nicolasstucki/scala-js/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nicolasstucki/scala-js/teams","hooks_url":"https://api.github.com/repos/nicolasstucki/scala-js/hooks","issue_events_url":"https://api.github.com/repos/nicolasstucki/scala-js/issues/events{/number}","events_url":"https://api.github.com/repos/nicolasstucki/scala-js/events","assignees_url":"https://api.github.com/repos/nicolasstucki/scala-js/assignees{/user}","branches_url":"https://api.github.com/repos/nicolasstucki/scala-js/branches{/branch}","tags_url":"https://api.github.com/repos/nicolasstucki/scala-js/tags","blobs_url":"https://api.github.com/repos/nicolasstucki/scala-js/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nicolasstucki/scala-js/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nicolasstucki/scala-js/git/refs{/sha}","trees_url":"https://api.github.com/repos/nicolasstucki/scala-js/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nicolasstucki/scala-js/statuses/{sha}","languages_url":"https://api.github.com/repos/nicolasstucki/scala-js/languages","stargazers_url":"https://api.github.com/repos/nicolasstucki/scala-js/stargazers","contributors_url":"https://api.github.com/repos/nicolasstucki/scala-js/contributors","subscribers_url":"https://api.github.com/repos/nicolasstucki/scala-js/subscribers","subscription_url":"https://api.github.com/repos/nicolasstucki/scala-js/subscription","commits_url":"https://api.github.com/repos/nicolasstucki/scala-js/commits{/sha}","git_commits_url":"https://api.github.com/repos/nicolasstucki/scala-js/git/commits{/sha}","comments_url":"https://api.github.com/repos/nicolasstucki/scala-js/comments{/number}","issue_comment_url":"https://api.github.com/repos/nicolasstucki/scala-js/issues/comments{/number}","contents_url":"https://api.github.com/repos/nicolasstucki/scala-js/contents/{+path}","compare_url":"https://api.github.com/repos/nicolasstucki/scala-js/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nicolasstucki/scala-js/merges","archive_url":"https://api.github.com/repos/nicolasstucki/scala-js/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nicolasstucki/scala-js/downloads","issues_url":"https://api.github.com/repos/nicolasstucki/scala-js/issues{/number}","pulls_url":"https://api.github.com/repos/nicolasstucki/scala-js/pulls{/number}","milestones_url":"https://api.github.com/repos/nicolasstucki/scala-js/milestones{/number}","notifications_url":"https://api.github.com/repos/nicolasstucki/scala-js/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nicolasstucki/scala-js/labels{/name}","releases_url":"https://api.github.com/repos/nicolasstucki/scala-js/releases{/id}","created_at":"2015-05-06T15:41:56Z","updated_at":"2015-05-06T15:41:58Z","pushed_at":"2015-10-15T15:14:14Z","git_url":"git://github.com/nicolasstucki/scala-js.git","ssh_url":"git@github.com:nicolasstucki/scala-js.git","clone_url":"https://github.com/nicolasstucki/scala-js.git","svn_url":"https://github.com/nicolasstucki/scala-js","homepage":"http://www.scala-js.org/","size":9975,"stargazers_count":0,"watchers_count":0,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"scala-js:master","ref":"master","sha":"ad624c7787e4606b30b7a5fa12a2f333c2aa1010","user":{"login":"scala-js","id":6039724,"avatar_url":"https://avatars.githubusercontent.com/u/6039724?v=3","gravatar_id":"","url":"https://api.github.com/users/scala-js","html_url":"https://github.com/scala-js","followers_url":"https://api.github.com/users/scala-js/followers","following_url":"https://api.github.com/users/scala-js/following{/other_user}","gists_url":"https://api.github.com/users/scala-js/gists{/gist_id}","starred_url":"https://api.github.com/users/scala-js/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scala-js/subscriptions","organizations_url":"https://api.github.com/users/scala-js/orgs","repos_url":"https://api.github.com/users/scala-js/repos","events_url":"https://api.github.com/users/scala-js/events{/privacy}","received_events_url":"https://api.github.com/users/scala-js/received_events","type":"Organization","site_admin":false},"repo":{"id":8272315,"name":"scala-js","full_name":"scala-js/scala-js","owner":{"login":"scala-js","id":6039724,"avatar_url":"https://avatars.githubusercontent.com/u/6039724?v=3","gravatar_id":"","url":"https://api.github.com/users/scala-js","html_url":"https://github.com/scala-js","followers_url":"https://api.github.com/users/scala-js/followers","following_url":"https://api.github.com/users/scala-js/following{/other_user}","gists_url":"https://api.github.com/users/scala-js/gists{/gist_id}","starred_url":"https://api.github.com/users/scala-js/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scala-js/subscriptions","organizations_url":"https://api.github.com/users/scala-js/orgs","repos_url":"https://api.github.com/users/scala-js/repos","events_url":"https://api.github.com/users/scala-js/events{/privacy}","received_events_url":"https://api.github.com/users/scala-js/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scala-js/scala-js","description":"Scala.js, the Scala to JavaScript compiler","fork":false,"url":"https://api.github.com/repos/scala-js/scala-js","forks_url":"https://api.github.com/repos/scala-js/scala-js/forks","keys_url":"https://api.github.com/repos/scala-js/scala-js/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scala-js/scala-js/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scala-js/scala-js/teams","hooks_url":"https://api.github.com/repos/scala-js/scala-js/hooks","issue_events_url":"https://api.github.com/repos/scala-js/scala-js/issues/events{/number}","events_url":"https://api.github.com/repos/scala-js/scala-js/events","assignees_url":"https://api.github.com/repos/scala-js/scala-js/assignees{/user}","branches_url":"https://api.github.com/repos/scala-js/scala-js/branches{/branch}","tags_url":"https://api.github.com/repos/scala-js/scala-js/tags","blobs_url":"https://api.github.com/repos/scala-js/scala-js/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scala-js/scala-js/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scala-js/scala-js/git/refs{/sha}","trees_url":"https://api.github.com/repos/scala-js/scala-js/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scala-js/scala-js/statuses/{sha}","languages_url":"https://api.github.com/repos/scala-js/scala-js/languages","stargazers_url":"https://api.github.com/repos/scala-js/scala-js/stargazers","contributors_url":"https://api.github.com/repos/scala-js/scala-js/contributors","subscribers_url":"https://api.github.com/repos/scala-js/scala-js/subscribers","subscription_url":"https://api.github.com/repos/scala-js/scala-js/subscription","commits_url":"https://api.github.com/repos/scala-js/scala-js/commits{/sha}","git_commits_url":"https://api.github.com/repos/scala-js/scala-js/git/commits{/sha}","comments_url":"https://api.github.com/repos/scala-js/scala-js/comments{/number}","issue_comment_url":"https://api.github.com/repos/scala-js/scala-js/issues/comments{/number}","contents_url":"https://api.github.com/repos/scala-js/scala-js/contents/{+path}","compare_url":"https://api.github.com/repos/scala-js/scala-js/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scala-js/scala-js/merges","archive_url":"https://api.github.com/repos/scala-js/scala-js/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scala-js/scala-js/downloads","issues_url":"https://api.github.com/repos/scala-js/scala-js/issues{/number}","pulls_url":"https://api.github.com/repos/scala-js/scala-js/pulls{/number}","milestones_url":"https://api.github.com/repos/scala-js/scala-js/milestones{/number}","notifications_url":"https://api.github.com/repos/scala-js/scala-js/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scala-js/scala-js/labels{/name}","releases_url":"https://api.github.com/repos/scala-js/scala-js/releases{/id}","created_at":"2013-02-18T16:33:55Z","updated_at":"2015-10-15T05:06:12Z","pushed_at":"2015-10-15T15:14:15Z","git_url":"git://github.com/scala-js/scala-js.git","ssh_url":"git@github.com:scala-js/scala-js.git","clone_url":"https://github.com/scala-js/scala-js.git","svn_url":"https://github.com/scala-js/scala-js","homepage":"http://www.scala-js.org/","size":17837,"stargazers_count":1875,"watchers_count":1875,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":165,"mirror_url":null,"open_issues_count":73,"forks":165,"open_issues":73,"watchers":1875,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/1959"},"html":{"href":"https://github.com/scala-js/scala-js/pull/1959"},"issue":{"href":"https://api.github.com/repos/scala-js/scala-js/issues/1959"},"comments":{"href":"https://api.github.com/repos/scala-js/scala-js/issues/1959/comments"},"review_comments":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/1959/comments"},"review_comment":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/1959/commits"},"statuses":{"href":"https://api.github.com/repos/scala-js/scala-js/statuses/cfdf844b0d82716f23a15b7be920f81fdf8917ae"}}}}