All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalajs.linker.NodeIRContainer.scala Maven / Gradle / Ivy

The newest version!
/*
 * Scala.js (https://www.scala-js.org/)
 *
 * Copyright EPFL.
 *
 * Licensed under Apache License 2.0
 * (https://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package org.scalajs.linker

import scala.concurrent._

import scala.util.{Success, Failure}

import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import scala.scalajs.js.typedarray._
import scala.scalajs.js.typedarray.TypedArrayBufferOps._

import java.nio._

import org.scalajs.linker.interface.{IRContainer, IRFile}
import org.scalajs.linker.interface.unstable.IRContainerImpl
import org.scalajs.linker.standard.MemIRFileImpl

object NodeIRContainer {
  import NodeFS._

  def fromClasspath(classpath: Seq[String])(
      implicit ec: ExecutionContext): Future[(Seq[IRContainer], Seq[String])] = {
    Future.traverse(classpath) { entry =>
      cbFuture[Stats](stat(entry, _)).transformWith {
        case Success(stat) if stat.isDirectory() =>
          fromDirectory(entry)

        case Success(stat) if entry.endsWith(".jar") =>
          val c = new NodeJarIRContainer(entry, stat.mtime.toOption)
          Future.successful(Seq((c, entry)))

        case Success(_) =>
          throw new IllegalArgumentException("Illegal classpath entry: " + entry)

        case Failure(js.JavaScriptException(e: js.Error)) if isNotFound(e) =>
          Future.successful(Nil)

        case Failure(t) =>
          throw t
      }
    }.map(_.flatten.unzip)
  }

  private def fromDirectory(dir: String)(
      implicit ec: ExecutionContext): Future[Seq[(IRContainer, String)]] = {
    cbFuture[js.Array[Dirent]](readdir(dir, ReadDirOpt, _)).flatMap { entries =>
      val (dirs, files) = entries.toSeq.partition(_.isDirectory())

      val subdirFiles = Future.traverse(dirs) { e =>
        fromDirectory(join(dir, e.name))
      }

      val irFileNames = files.map(_.name).filter(_.endsWith(".sjsir"))
      val directFiles = Future.traverse(irFileNames) { n =>
        val path = join(dir, n)
        NodeIRFile(path).map(f => (IRContainer.fromIRFile(f), path))
      }

      for {
        sdf <- subdirFiles
        df <- directFiles
      } yield sdf.flatten ++ df
    }
  }

  private def isNotFound(e: js.Error): Boolean =
    (e.asInstanceOf[js.Dynamic].code: Any) == "ENOENT"

  private final class NodeJarIRContainer(path: String, version: Option[js.Date])
      extends IRContainerImpl(path, NodeIRFile.dateToVersion(version)) {
    import NodeFS._

    def sjsirFiles(implicit ec: ExecutionContext): Future[List[IRFile]] = {
      for {
        arr <- cbFuture[Uint8Array](readFile(path, _))
        zip <- JSZip.loadAsync(arr).toFuture
        files <- loadFromZip(zip)
      } yield {
        files.toList
      }
    }

    private def loadFromZip(obj: JSZip.JSZip)(
        implicit ec: ExecutionContext): Future[Iterator[IRFile]] = {
      val entries = obj.files.valuesIterator
        .filter(e => e.name.endsWith(".sjsir") && !e.dir)

      Future.traverse(entries) { entry =>
        entry.async(JSZipInterop.arrayBuffer).toFuture.map { buf =>
          new MemIRFileImpl(s"${this.path}:${entry.name}", version, new Int8Array(buf).toArray)
        }
      }
    }
  }

  private object JSZipInterop {
    val arrayBuffer: String = "arraybuffer"
  }

  @js.native
  @JSImport("jszip", JSImport.Default)
  private object JSZip extends js.Object {
    trait JSZip extends js.Object {
      val files: js.Dictionary[ZipObject]
    }

    trait ZipObject extends js.Object {
      val name: String
      val dir: Boolean
      def async(tpe: JSZipInterop.arrayBuffer.type): js.Promise[ArrayBuffer]
    }

    def loadAsync(data: Uint8Array): js.Promise[JSZip] = js.native
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy