{"action":"created","comment":{"url":"https://api.github.com/repos/scala-js/scala-js/pulls/comments/43878793","id":43878793,"diff_hunk":"@@ -0,0 +1,544 @@\n+package org.scalajs.junit.plugin\n+\n+import scala.language.reflectiveCalls\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+/** The Scala.js jUnit plugin is a way to overcome make class annotation\n+ *  information (usually accessed by reflection) of any test class accessible\n+ *  to the Scala.js testing framework.\n+ *\n+ *  As an example we take the following test class:\n+ *  {{{\n+ *  class Foo {\n+ *    @Before def before(): Unit = {\n+ *      // Initialize the instance before the tests\n+ *    }\n+ *    @Test def bar(): Unit = {\n+ *      // assert some stuff\n+ *    }\n+ *    @Ignore(\"baz not implemented yet\") @Test def baz(): Unit = {\n+ *      // assert some other stuff\n+ *    }\n+ *  }\n+ *\n+ *  object Foo {\n+ *    @BeforeClass def beforeClass(): Unit = {\n+ *      // Initialize some global state for the tests.\n+ *    }\n+ *  }\n+ *  }}}\n+ *\n+ *  Will generate the following bootstrapper module:\n+ *\n+ *  {{{\n+ *  object Foo\\$scalajs\\$junit\\$bootstrapper extends org.scalajs.junit.JUnitTestBootstrapper {\n+ *\n+ *    def metadata(): JUnitClassMetadata = {\n+ *      new JUnitClassMetadata(\n+ *        classAnnotations = List(),\n+ *        moduleAnnotations = List(),\n+ *        classMethods = List(\n+ *            new JUnitMethodMetadata(name = \"before\",\n+ *                annotations = List(new Before)),\n+ *            new JUnitMethodMetadata(name = \"bar\",\n+ *                annotations = List(new Test)),\n+ *            new JUnitMethodMetadata(name = \"baz\",\n+ *                annotations = List(new Test, new Ignore(\"baz not implemented yet\")))\n+ *        ),\n+ *        moduleMethods(\n+ *            new JUnitMethodMetadata(name = \"beforeClass\",\n+ *                annotations = List(new BeforeClass)))\n+ *      )\n+ *    }\n+ *\n+ *    def newInstance(): AnyRef = new Foo()\n+ *\n+ *    def invoke(methodName: String): Unit = {\n+ *      if (methodName == \"0\") Foo.beforeClass()\n+ *      else throw new NoSuchMethodException(methodId)\n+ *    }\n+ *\n+ *    def invoke(instance: AnyRef, methodName: String): Unit = {\n+ *      if (methodName == \"before\") instance.asInstanceOf[Foo].before()\n+ *      else if (methodName == \"bar\") instance.asInstanceOf[Foo].bar()\n+ *      else if (methodName == \"baz\") instance.asInstanceOf[Foo].baz()\n+ *      else throw new NoSuchMethodException(methodId)\n+ *    }\n+ *  }\n+ *  }}}\n+ *  The test framework will identify `Foo\\$scalajs\\$junit\\$bootstrapper` as a test module\n+ *  because it extends `JUnitTestBootstrapper`. It will know which methods to run based\n+ *  on the info returned by Foo\\$scalajs\\$junit\\$bootstrapper.metadata,\n+ *  it will create new test instances using `Foo\\$scalajs\\$junit\\$bootstrapper.newInstance()`\n+ *  and it will invoke test methods using `scalajs\\$junit\\$invoke` methods on the\n+ *  module or instance.\n+ */\n+class ScalaJSJUnitPlugin(val global: Global) extends NscPlugin {\n+\n+  val name: String = \"Scala.js JUnit plugin\"\n+\n+  val components: List[NscPluginComponent] =\n+    List(ScalaJSJUnitPluginComponent)\n+\n+  val description: String = \"Makes JUnit test classes invokable in Scala.js\"\n+\n+  object ScalaJSJUnitPluginComponent\n+    extends plugins.PluginComponent with transform.Transform {\n+\n+    protected def newTransformer(\n+        unit: global.CompilationUnit): global.Transformer = {\n+      new ScalaJSJUnitPluginTransformer\n+    }\n+\n+    val global: Global = ScalaJSJUnitPlugin.this.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+      import rootMirror.getRequiredClass\n+\n+      /* Hack to be able to access `registerModuleExports` of ScalaJSPlugin\n+       * without modifying it.\n+       */\n+      private lazy val scalaJSPlugin = {\n+        type ScalaJSPlugin = NscPlugin {\n+          def PrepInteropComponent: AnyRef {\n+            def registerModuleExports(sym: Symbol): Unit\n+          }\n+        }\n+        global.plugins.collectFirst {\n+          case pl if pl.getClass.getName == \"org.scalajs.core.compiler.ScalaJSPlugin\" =>\n+            pl.asInstanceOf[ScalaJSPlugin]\n+        }.getOrElse {\n+          throw new Exception(\n+            \"The Scala.js JUnit plugin only works with the Scala.js plugin enabled.\")\n+        }\n+      }\n+\n+      private val testAnnotationSymbol =\n+        getRequiredClass(\"org.junit.Test\")\n+\n+      private val fixMethodOrderAnnotationSymbol =\n+        getRequiredClass(\"org.junit.FixMethodOrder\")\n+\n+      private val annotationWhiteList = List(\n+        testAnnotationSymbol,\n+        getRequiredClass(\"org.junit.Before\"),\n+        getRequiredClass(\"org.junit.After\"),\n+        getRequiredClass(\"org.junit.BeforeClass\"),\n+        getRequiredClass(\"org.junit.AfterClass\"),\n+        getRequiredClass(\"org.junit.Ignore\")\n+      )\n+\n+      private val jUnitClassMetadataType =\n+        getRequiredClass(\"org.scalajs.junit.JUnitClassMetadata\").toType\n+\n+      private val jUnitTestMetadataType =\n+        getRequiredClass(\"org.scalajs.junit.JUnitTestBootstrapper\").toType\n+\n+      private def jUnitMethodMetadataTypeTree =\n+        TypeTree(getRequiredClass(\"org.scalajs.junit.JUnitMethodMetadata\").toType)\n+\n+      override def transform(tree: Tree): Tree = tree match {\n+        case tree: PackageDef =>\n+          def isClassWithJUnitAnnotation(sym: Symbol): Boolean = sym match {\n+            case _:ClassSymbol | _:ModuleSymbol =>\n+              val hasAnnotationInClass = sym.selfType.members.exists {\n+                case mtdSym: MethodSymbol => hasAnnotation(mtdSym, testAnnotationSymbol)\n+                case _ => false\n+              }\n+              if (hasAnnotationInClass) true\n+              else sym.parentSymbols.headOption.fold(false)(isClassWithJUnitAnnotation)\n+\n+            case _ => false\n+          }\n+\n+          val bootstrappers = tree.stats.groupBy { // Group the class with its module\n+            case clDef: ClassDef => Some(clDef.name)\n+            case _               => None\n+          }.iterator.flatMap {\n+            case (Some(_), xs) if xs.exists(x => isClassWithJUnitAnnotation(x.symbol)) =>\n+              def isModule(cDef: ClassDef): Boolean =\n+                cDef.mods.hasFlag(Flags.MODULE)\n+              def isTestClass(cDef: ClassDef): Boolean = {\n+                !cDef.mods.hasFlag(Flags.MODULE) &&\n+                !cDef.mods.hasFlag(Flags.ABSTRACT) &&\n+                !cDef.mods.hasFlag(Flags.TRAIT)\n+              }\n+              // Get the class definition and do the transformation\n+              xs.collectFirst {\n+                case clDef: ClassDef if isTestClass(clDef) =>\n+                  // Get the module definition\n+                  val mlDefOption = xs collectFirst {\n+                    case clDef: ClassDef if isModule(clDef) => clDef\n+                  }\n+                  // Create a new module for the JUnit entry point.\n+                  mkBootstrapperClass(clDef, mlDefOption)\n+              }\n+\n+            case (_, xs) => None\n+          }\n+\n+          val newStats = tree.stats.map(transform) ++ bootstrappers\n+\n+          treeCopy.PackageDef(tree: Tree, tree.pid, newStats.toList)\n+\n+        case _ =>\n+          super.transform(tree)\n+      }\n+\n+      def mkBootstrapperClass(clazz: ClassDef, mlDefOption: Option[ClassDef]): ClassDef = {\n+        val bootSym = clazz.symbol.cloneSymbol\n+        val getJUnitMetadataDef = mkGetJUnitMetadataDef(clazz.symbol,\n+            mlDefOption.map(_.symbol))\n+        val newInstanceDef = genNewInstanceDef(clazz.symbol, bootSym)\n+        val invokeJUnitMethodDef = {\n+          val annotatedMethods = mlDefOption.fold(List.empty[MethodSymbol]){ ml =>\n+            jUnitAnnotatedMethods(ml.symbol.asClass)\n+          }\n+          mkInvokeJUnitMethodOnModuleDef(annotatedMethods, bootSym,\n+              mlDefOption.map(_.symbol))\n+        }\n+        val invokeJUnitMethodOnInstanceDef = {\n+          val annotatedMethods = jUnitAnnotatedMethods(clazz.symbol.asClass)\n+          mkInvokeJUnitMethodOnInstanceDef(annotatedMethods, bootSym,\n+            clazz.symbol)\n+        }\n+\n+        val bootBody =\n+          List(getJUnitMetadataDef, newInstanceDef, invokeJUnitMethodDef, invokeJUnitMethodOnInstanceDef)\n+        val bootParents = List(\n+          TypeTree(definitions.ObjectTpe),\n+          TypeTree(jUnitTestMetadataType)\n+        )\n+        val bootImpl =\n+          treeCopy.Template(clazz.impl, bootParents, clazz.impl.self, bootBody)\n+\n+        val bootName = newTypeName(clazz.name.toString + \"$scalajs$junit$bootstrapper\")\n+        val bootClazz = gen_mkClassDef_compat(Modifiers(Flags.MODULE),\n+          bootName, Nil, bootImpl)\n+        bootSym.flags += Flags.MODULE\n+        bootSym.withoutAnnotations\n+        bootSym.setName(bootName)\n+        val newClazzInfo = {\n+          val newParentsInfo = List(\n+            definitions.ObjectTpe,\n+            jUnitTestMetadataType\n+          )\n+          val decls = bootSym.info.decls\n+          decls.enter(getJUnitMetadataDef.symbol)\n+          decls.enter(newInstanceDef.symbol)\n+          decls.enter(invokeJUnitMethodDef.symbol)\n+          decls.enter(invokeJUnitMethodOnInstanceDef.symbol)\n+          ClassInfoType(newParentsInfo, decls, bootSym.info.typeSymbol)\n+        }\n+        bootSym.setInfo(newClazzInfo)\n+        scalaJSPlugin.PrepInteropComponent.registerModuleExports(bootSym)\n+        bootClazz.setSymbol(bootSym)\n+\n+        bootClazz\n+      }\n+\n+      def jUnitAnnotatedMethods(sym: Symbol): List[MethodSymbol] = {\n+        sym.selfType.members.collect {\n+          case m: MethodSymbol if hasJUnitMethodAnnotation(m) => m\n+        }.toList\n+      }\n+\n+      def getMethods(clazzDef: ClassDef): List[DefDef] =\n+        clazzDef.impl.body.collect { case dDef: DefDef => dDef }\n+\n+      /**\n+       * This method generates a method that invokes a test method in the module\n+       * given its name. These methods have no parameters.\n+       *\n+       * Example:\n+       * {{{\n+       * object Foo {\n+       *   @BeforeClass def bar(): Unit\n+       *   @AfterClass def baz(): Unit\n+       * }\n+       * object Foo\\$scalajs\\$junit\\$bootstrapper\n+       *   // This is the method generated by mkInvokeJUnitMethodOnModuleDef\n+       *   def invoke(methodName: String): Unit = {\n+       *     if (methodName == \"bar\") Foo.bar()\n+       *     else if (methodName == \"baz\") Foo.baz()\n+       *     else throw new NoSuchMethodException(methodName + \" not found\")\n+       *   }\n+       * }\n+       * }}}\n+       */\n+      def mkInvokeJUnitMethodOnModuleDef(methods: List[MethodSymbol],\n+          bootSym: Symbol, mlClassSym: Option[Symbol]): DefDef = {\n+\n+        val invokeJUnitMethodSym = bootSym.newMethod(newTermName(\"invoke\"))\n+\n+        val paramSym =\n+          invokeJUnitMethodSym.newValueParameter(newTermName(\"methodName\"))\n+        paramSym.setInfo(typeOf[java.lang.String])\n+\n+        invokeJUnitMethodSym.setInfo(\n+          MethodType(List(paramSym), definitions.UnitTpe))\n+\n+        val methodNotFound = {\n+          val msg = gen.mkMethodCall(paramSym, nme.PLUS, Nil,\n+            List(Literal(Constant(\" not found\"))))\n+          val exception = mkNewInstance[NoSuchMethodException](List(msg))\n+          Throw(exception)\n+        }\n+\n+        val invokeJUnitMethodRhs0 = {\n+          methods.foldRight[Tree](methodNotFound) {\n+            case (methodSymbol, acc) =>\n+              val methodSymbolLocal = {\n+                mlClassSym.fold[Symbol](methodSymbol) { sym =>\n+                  methodSymbol.cloneSymbol(newOwner = sym)\n+                }\n+              }\n+              val headMethodId = Literal(Constant(methodSymbol.nameString))\n+              If(gen.mkMethodCall(paramSym, nme.equals_, Nil,\n+                List(headMethodId)), gen.mkMethodCall(methodSymbolLocal, Nil),acc)\n+          }\n+        }\n+        val invokeJUnitMethodRhs =\n+          atOwner(invokeJUnitMethodSym)(typer.typed(invokeJUnitMethodRhs0))\n+        val param = newValDef_compat(paramSym, EmptyTree)()\n+        typer.typedDefDef(newDefDef_compat(invokeJUnitMethodSym, invokeJUnitMethodRhs)(\n+            vparamss = List(List(param))))\n+      }\n+\n+      /**\n+       * This method generates a method that invokes a test method in the class\n+       * given its name. These methods have no parameters.\n+       *\n+       * Example:\n+       * {{{\n+       * class Foo {\n+       *   @Test def bar(): Unit\n+       *   @Test def baz(): Unit\n+       * }\n+       * object Foo\\$scalajs\\$junit\\$bootstrapper\n+       *   // This is the method generated by mkInvokeJUnitMethodOnInstanceDef\n+       *   def invoke(instance: AnyRef, methodName: String): Unit = {\n+       *     if (methodName == \"bar\") instance.asInstanceOf[Foo].bar()\n+       *     else if (methodName == \"baz\") instance.asInstanceOf[Foo].baz()\n+       *     else throw new NoSuchMethodException(methodName + \" not found\")\n+       *   }\n+       * }\n+       * }}}\n+       */\n+      def mkInvokeJUnitMethodOnInstanceDef(methods: List[MethodSymbol],\n+          classSym: Symbol, refClassSym: Symbol): DefDef = {\n+        val invokeJUnitMethodSym = classSym.newMethod(newTermName(\"invoke\"))\n+\n+        val instanceParamSym =\n+          invokeJUnitMethodSym.newValueParameter(newTermName(\"instance\"))\n+        instanceParamSym.setInfo(definitions.ObjectTpe)\n+\n+        val idParamSym =\n+          invokeJUnitMethodSym.newValueParameter(newTermName(\"methodName\"))\n+        idParamSym.setInfo(definitions.StringTpe)","path":"junit-plugin/src/main/scala/org/scalajs/junit/plugin/ScalaJSJUnitPlugin.scala","position":348,"original_position":348,"commit_id":"b0130baba64bf8ccec9d4e97fb0e9abe75b98407","original_commit_id":"b0130baba64bf8ccec9d4e97fb0e9abe75b98407","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":"`StringTpe` does not exist in Scala 2.10. You'll have to use `StringClass.toType`.","created_at":"2015-11-04T13:19:51Z","updated_at":"2015-11-04T13:19:51Z","html_url":"https://github.com/scala-js/scala-js/pull/1959#discussion_r43878793","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/43878793"},"html":{"href":"https://github.com/scala-js/scala-js/pull/1959#discussion_r43878793"},"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-11-04T13:19:51Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f453c16c4577a485ad69cef8afdf2b331cce2ea7","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/b0130baba64bf8ccec9d4e97fb0e9abe75b98407","head":{"label":"nicolasstucki:junit-test","ref":"junit-test","sha":"b0130baba64bf8ccec9d4e97fb0e9abe75b98407","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-11-04T11:11:58Z","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":10230,"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":"4577c0efae6dc847329ed64507424399523bf017","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-11-04T12:18:57Z","pushed_at":"2015-11-04T13:04:46Z","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":18119,"stargazers_count":1902,"watchers_count":1902,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":168,"mirror_url":null,"open_issues_count":82,"forks":168,"open_issues":82,"watchers":1902,"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/b0130baba64bf8ccec9d4e97fb0e9abe75b98407"}}}}