refactor(js): NPM modules resolution (#722)

Refactored NPM modules resolution
This commit is contained in:
InversionSpaces 2023-05-31 12:00:13 +02:00 committed by GitHub
parent 19c4e509a8
commit df19ec6734
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 43 deletions

View File

@ -2,7 +2,7 @@ package aqua
import cats.effect.kernel.Async import cats.effect.kernel.Async
import fs2.io.file.{Files, Path} import fs2.io.file.{Files, Path}
import aqua.js.{Meta, Module} import aqua.js.Npm
import scribe.Logging import scribe.Logging
import cats.syntax.applicative.* import cats.syntax.applicative.*
import cats.syntax.apply.* import cats.syntax.apply.*
@ -14,51 +14,50 @@ import scala.util.Try
object PlatformPackagePath extends Logging { object PlatformPackagePath extends Logging {
// it could be global installed aqua and local installed, different paths for this // get path of nth parent strictly
def getPackagePath[F[_]: Async](path: String): F[Path] = { private def parentStrict(n: Int)(path: Path): Option[Path] =
val meta = Meta.metaUrl if (n <= 0) Some(path)
val req = Module.createRequire(meta) else (0 until n).foldLeft(Option(path))((p, _) => p.flatMap(_.parent))
Try {
// this can throw an error, global or local project path
val builtinPath = Path(req.resolve("@fluencelabs/aqua-lib/builtin.aqua").toString)
val rootProjectPath = builtinPath.resolve("../../../..")
// hack, check if it is a local dependency or global
val filePath = rootProjectPath.resolve(path)
Files[F].exists(filePath).map {
// if not exists, it should be local dependency, check in node_modules
case false => rootProjectPath.resolve("node_modules/@fluencelabs/aqua").resolve(path)
case true => filePath
} // get path of nth parent relatively
}.getOrElse { private def parentRelative(n: Int)(path: Path): Path =
// we don't care about path if there is no builtins, but must write an error if (n <= 0) path
logger.error("Unexpected. Cannot find project path") else path.resolve((0 until n).map(_ => "../").mkString)
Path(path).pure[F]
// could throw an error
private def builtinPath = Path(Npm.resolveModule("@fluencelabs/aqua-lib/builtin.aqua"))
// it could be global installed aqua and local installed, different paths for this
def getPackagePath[F[_]: Async](path: String): F[Path] = Try {
val rootProjectPath = parentRelative(4)(builtinPath)
// hack, check if it is a local dependency or global
val filePath = rootProjectPath.resolve(path)
Files[F].exists(filePath).map {
// if not exists, it should be local dependency, check in node_modules
case false => rootProjectPath.resolve("node_modules/@fluencelabs/aqua").resolve(path)
case true => filePath
} }
}.getOrElse {
// we don't care about path if there is no builtins, but must write an error
logger.error("Unexpected. Cannot find project path")
Path(path).pure[F]
} }
// get path to node modules if there is `aqua-lib` module with `builtin.aqua` in it // get path to node modules if there is `aqua-lib` module with `builtin.aqua` in it
def getGlobalNodeModulePath: List[Path] = { def getGlobalNodeModulePath: List[Path] = Try {
val meta = Meta.metaUrl // hack
val req = Module.createRequire(meta) val globalAquaPath = parentStrict(3)(builtinPath)
Try {
// this can throw an error
val pathStr = req.resolve("@fluencelabs/aqua-lib/builtin.aqua").toString
// hack
val globalAquaPath = Path(pathStr).parent.flatMap(_.parent.flatMap(_.parent))
// Also hack. If we found installed `aqua-lib`, it should be in `node_modules` global path. // Also hack. If we found installed `aqua-lib`, it should be in `node_modules` global path.
// In global `node_modules` could be installed aqua libs and we must use them, // In global `node_modules` could be installed aqua libs and we must use them,
// if they were imported in aqua files // if they were imported in aqua files
val globalNodeModulesPath = val globalNodeModulesPath = globalAquaPath.flatMap(parentStrict(3))
globalAquaPath.flatMap(_.parent.flatMap(_.parent.flatMap(_.parent)))
globalAquaPath.toList ++ globalNodeModulesPath.toList
}.getOrElse {
// we don't care about path if there is no builtins, but must write an error
logger.error("Unexpected. Cannot find 'aqua-lib' dependency with `builtin.aqua` in it")
Nil
}
globalAquaPath.toList ++ globalNodeModulesPath.toList
}.getOrElse {
// we don't care about path if there is no builtins, but must write an error
logger.error("Unexpected. Cannot find 'aqua-lib' dependency with `builtin.aqua` in it")
Nil
} }
} }

View File

@ -12,14 +12,24 @@ object Meta {
val metaUrl: String = js.native val metaUrl: String = js.native
} }
// Require function from javascript
trait Require extends js.Object {
// resolve path to module
def resolve(request: String): String
}
@js.native @js.native
@JSImport("module", JSImport.Namespace) @JSImport("module", JSImport.Namespace)
object Module extends js.Object { object Module extends js.Object {
// make it possible to use `require` in ES module type // make it possible to use `require` in ES module type
def createRequire(str: String): Require = js.native def createRequire(filename: String): Require = js.native
} }
trait Require extends js.Object { object Npm {
def resolve(str: String): Any private def require = Module.createRequire(Meta.metaUrl)
// Resolve path to module
def resolveModule(path: String): String = require.resolve(path)
} }