diff --git a/build.sbt b/build.sbt index f1354ed4..3b4be66d 100644 --- a/build.sbt +++ b/build.sbt @@ -100,8 +100,7 @@ val sharedJVMSettings: List[Def.Setting[_]] = List( ) ++ mimaEnable val sharedJSSettings: List[Def.Setting[_]] = List( skipIdeaSettings, - crossScalaVersions := allScalaVersions.filterNot(_.startsWith("0.")), - scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)) + crossScalaVersions := allScalaVersions.filterNot(_.startsWith("0.")) ) val sharedJSConfigure: Project => Project = _.disablePlugins(MimaPlugin) diff --git a/munit/js/src/main/scala/java/io/File.scala b/munit/js/src/main/scala/java/io/File.scala index 16b690b1..3b1a6ff9 100644 --- a/munit/js/src/main/scala/java/io/File.scala +++ b/munit/js/src/main/scala/java/io/File.scala @@ -2,8 +2,8 @@ package java.io import java.net.URI import java.nio.file.Path - -import munit.internal.{JSIO, JSPath, NodeNIOPath} +import munit.internal.JSIO +import munit.internal.NodeNIOPath // obtained implementation by experimentation on the JDK. class File(path: String) { @@ -55,8 +55,12 @@ class File(path: String) { object File { def listRoots(): Array[File] = Array( new File( - if (JSIO.isNode) JSPath.parse(JSPath.resolve()).root - else "/" + JSIO.path match { + case Some(p) => p.parse(p.resolve()).root.asInstanceOf[String] + case None => "/" + } + // if (JSIO.isNode) JSPath.parse(JSPath.resolve()).root + // else "/" ) ) @@ -64,10 +68,14 @@ object File { separator.charAt(0) def separator: String = - if (JSIO.isNode) JSPath.sep - else "/" + JSIO.path match { + case Some(p) => p.sep.asInstanceOf[String] + case None => "/" + } def pathSeparator: String = - if (JSIO.isNode) JSPath.delimiter - else ":" + JSIO.path match { + case Some(p) => p.delimeter.asInstanceOf[String] + case None => ":" + } } diff --git a/munit/js/src/main/scala/java/nio/file/Files.scala b/munit/js/src/main/scala/java/nio/file/Files.scala index a0e8b9a3..6583f4ba 100644 --- a/munit/js/src/main/scala/java/nio/file/Files.scala +++ b/munit/js/src/main/scala/java/nio/file/Files.scala @@ -1,9 +1,10 @@ package java.nio.file +import scala.scalajs.js import java.{util => ju} import java.nio.charset.StandardCharsets -import munit.internal.{JSFs, JSIO} +import munit.internal.JSIO import scala.collection.JavaConverters._ @@ -13,8 +14,12 @@ object Files { val text = new String(bytes, StandardCharsets.UTF_8) text.linesIterator.toSeq.asJava } - def readAllBytes(path: Path): Array[Byte] = JSIO.inNode { - val jsArray = JSFs.readFileSync(path.toString) + def readAllBytes(path: Path): Array[Byte] = { + val jsArray = JSIO.fs match { + case Some(fs) => + fs.readFileSync(path.toString).asInstanceOf[js.Array[Int]] + case None => new js.Array[Int](0) + } val len = jsArray.length val result = new Array[Byte](len) var curr = 0 diff --git a/munit/js/src/main/scala/munit/internal/JSIO.scala b/munit/js/src/main/scala/munit/internal/JSIO.scala index 5453e04c..162a175d 100644 --- a/munit/js/src/main/scala/munit/internal/JSIO.scala +++ b/munit/js/src/main/scala/munit/internal/JSIO.scala @@ -1,117 +1,38 @@ package munit.internal import scala.scalajs.js -import scala.scalajs.js.annotation.JSImport -import scala.scalajs.js.annotation.JSImport.Namespace - -/** - * Facade for the native nodejs process API - * - * The process object is a global that provides information about, and - * control over, the current Node.js process. As a global, it is always - * available to Node.js applications without using require(). - * - * @see https://nodejs.org/api/process.html - */ -@js.native -trait JSProcess extends js.Any { - def cwd(): String = js.native -} - -/** - * Facade for native nodejs module "fs". - * - * @see https://nodejs.org/api/fs.html - */ -@js.native -@JSImport("fs", Namespace) -object JSFs extends js.Any { - - /** - * Returns the file contents as Buffer using blocking apis. - * - * NOTE: The actual return value is a Node.js buffer and not js.Array[Int]. - * However, both support .length and angle bracket access (foo[1]). - */ - def readFileSync(path: String): js.Array[Int] = js.native - - /** Returns the file contents as String using blocking apis */ - def readFileSync(path: String, encoding: String): String = js.native - - /** Writes file contents using blocking apis */ - def writeFileSync(path: String, buffer: js.Array[Int]): Unit = js.native - - /** Returns an array of filenames excluding '.' and '..'. */ - def readdirSync(path: String): js.Array[String] = js.native - - /** Returns an fs.Stats for path. */ - def lstatSync(path: String): JSStats = js.native - - /** Returns true if the file exists, false otherwise. */ - def existsSync(path: String): Boolean = js.native - - /** Synchronously creates a directory. */ - def mkdirSync(path: String): Unit = js.native -} - -/** - * Facade for nodejs class fs.Stats. - * - * @see https://nodejs.org/api/fs.html#fs_class_fs_stats - */ -@js.native -@JSImport("fs", Namespace) -class JSStats extends js.Any { - def isFile(): Boolean = js.native - def isDirectory(): Boolean = js.native -} - -/** - * Facade for native nodejs module "path". - * - * @see https://nodejs.org/api/path.html - */ -@js.native -@JSImport("path", Namespace) -object JSPath extends js.Any { - def sep: String = js.native - def delimiter: String = js.native - def isAbsolute(path: String): Boolean = js.native - def parse(path: String): JSPath.type = js.native - def resolve(paths: String*): String = js.native - def normalize(path: String): String = js.native - def basename(path: String): String = js.native - def dirname(path: String): String = js.native - def root: String = js.native - def relative(from: String, to: String): String = js.native - def join(first: String, more: String*): String = js.native -} +import scala.util.Try object JSIO { - private[internal] val process: JSProcess = - js.Dynamic.global.process.asInstanceOf[JSProcess] - def isNode: Boolean = - !js.isUndefined(process) && !js.isUndefined(process.cwd()) - def inNode[T](f: => T): T = - if (JSIO.isNode) f - else { - throw new IllegalStateException( - "This operation is not supported in this environment." - ) - } + private def require(module: String): Option[js.Dynamic] = { + Try(js.Dynamic.global.require(module)).toOption + } + val process: Option[js.Dynamic] = require("process") + val path: Option[js.Dynamic] = require("path") + val fs: Option[js.Dynamic] = require("fs") def cwd(): String = - if (isNode) process.cwd() - else "/" + process match { + case Some(p) => p.cwd().asInstanceOf[String] + case None => "/" + } def exists(path: String): Boolean = - if (isNode) JSFs.existsSync(path) - else false + fs match { + case Some(f) => f.existsSync(path).asInstanceOf[Boolean] + case None => false + } def isFile(path: String): Boolean = - exists(path) && JSFs.lstatSync(path).isFile() + exists(path) && (fs match { + case Some(f) => f.lstatSync(path).isFile().asInstanceOf[Boolean] + case None => false + }) def isDirectory(path: String): Boolean = - exists(path) && JSFs.lstatSync(path).isDirectory() + exists(path) && (fs match { + case Some(f) => f.lstatSync(path).isDirectory().asInstanceOf[Boolean] + case None => false + }) } diff --git a/munit/js/src/main/scala/munit/internal/NodeNIOPath.scala b/munit/js/src/main/scala/munit/internal/NodeNIOPath.scala index 94d3d385..7ffd2243 100644 --- a/munit/js/src/main/scala/munit/internal/NodeNIOPath.scala +++ b/munit/js/src/main/scala/munit/internal/NodeNIOPath.scala @@ -29,9 +29,10 @@ case class NodeNIOPath(filename: String) extends Path { ) override def toFile: File = new File(filename) - override def isAbsolute: Boolean = - if (JSIO.isNode) JSPath.isAbsolute(filename) - else filename.startsWith(File.separator) + override def isAbsolute: Boolean = JSIO.path match { + case Some(path) => path.isAbsolute(filename).asInstanceOf[Boolean] + case None => filename.startsWith(File.separator) + } override def getName(index: Int): Path = NodeNIOPath( filename @@ -40,12 +41,29 @@ case class NodeNIOPath(filename: String) extends Path { .getOrElse(throw new IllegalArgumentException) ) override def getParent: Path = - NodeNIOPath(JSPath.dirname(filename)) + JSIO.path match { + case Some(path) => + NodeNIOPath(path.dirname(filename).asInstanceOf[String]) + case None => + throw new UnsupportedOperationException( + "Path.getParent() is only supported in Node.js" + ) + } + override def toAbsolutePath: Path = if (isAbsolute) this else NodeNIOPath.workingDirectory.resolve(this) override def relativize(other: Path): Path = - NodeNIOPath(JSPath.relative(filename, other.toString)) + JSIO.path match { + case Some(path) => + NodeNIOPath( + path.relative(filename, other.toString()).asInstanceOf[String] + ) + case None => + throw new UnsupportedOperationException( + "Path.relativize() is only supported in Node.js" + ) + } override def getNameCount: Int = { val strippeddrive = if ((filename.length > 1) && (filename(1) == ':')) filename.substring(2) @@ -57,13 +75,26 @@ case class NodeNIOPath(filename: String) extends Path { } override def toUri: URI = toFile.toURI override def getFileName: Path = - NodeNIOPath(JSPath.basename(filename)) + JSIO.path match { + case Some(path) => + NodeNIOPath(path.basename(filename).asInstanceOf[String]) + case None => + throw new UnsupportedOperationException( + "Path.getFileName() is only supported in Node.js" + ) + } override def getRoot: Path = if (!isAbsolute) null else NodeNIOPath(File.separator) override def normalize(): Path = - if (JSIO.isNode) NodeNIOPath(JSPath.normalize(filename)) - else this + JSIO.path match { + case Some(path) => + NodeNIOPath(path.normalize(filename).asInstanceOf[String]) + case None => + throw new UnsupportedOperationException( + "Path.normalize() is only supported in Node.js" + ) + } override def endsWith(other: Path): Boolean = endsWith(other.toString) override def endsWith(other: String): Boolean = @@ -76,13 +107,33 @@ case class NodeNIOPath(filename: String) extends Path { override def resolveSibling(other: Path): Path = resolveSibling(other.toString) override def resolveSibling(other: String): Path = - adjustResolvedPath( - NodeNIOPath(JSPath.resolve(JSPath.dirname(filename), other)) - ) + JSIO.path match { + case Some(path) => + adjustResolvedPath( + NodeNIOPath( + path + .resolve(path.dirname(filename).asInstanceOf[String], other) + .asInstanceOf[String] + ) + ) + case None => + throw new UnsupportedOperationException( + "Path.normalize() is only supported in Node.js" + ) + } override def resolve(other: Path): Path = resolve(other.toString) override def resolve(other: String): Path = - adjustResolvedPath(NodeNIOPath(JSPath.resolve(filename, other))) + JSIO.path match { + case Some(path) => + adjustResolvedPath( + NodeNIOPath(path.resolve(filename, other).asInstanceOf[String]) + ) + case None => + throw new UnsupportedOperationException( + "Path.normalize() is only supported in Node.js" + ) + } override def startsWith(other: Path): Boolean = startsWith(other.toString) override def startsWith(other: String): Boolean =