From df19ec67347d461146190ccf1de5e0e06813c1d2 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Wed, 31 May 2023 12:00:13 +0200 Subject: [PATCH] refactor(js): NPM modules resolution (#722) Refactored NPM modules resolution --- .../main/scala/aqua/PlatformPackagePath.scala | 79 +++++++++---------- .../src/main/scala/aqua/js/Npm.scala | 16 +++- 2 files changed, 52 insertions(+), 43 deletions(-) diff --git a/io/.js/src/main/scala/aqua/PlatformPackagePath.scala b/io/.js/src/main/scala/aqua/PlatformPackagePath.scala index 6e31646c..ac5e38e6 100644 --- a/io/.js/src/main/scala/aqua/PlatformPackagePath.scala +++ b/io/.js/src/main/scala/aqua/PlatformPackagePath.scala @@ -2,7 +2,7 @@ package aqua import cats.effect.kernel.Async import fs2.io.file.{Files, Path} -import aqua.js.{Meta, Module} +import aqua.js.Npm import scribe.Logging import cats.syntax.applicative.* import cats.syntax.apply.* @@ -14,51 +14,50 @@ import scala.util.Try object PlatformPackagePath extends Logging { - // it could be global installed aqua and local installed, different paths for this - def getPackagePath[F[_]: Async](path: String): F[Path] = { - val meta = Meta.metaUrl - val req = Module.createRequire(meta) - 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 strictly + private def parentStrict(n: Int)(path: Path): Option[Path] = + if (n <= 0) Some(path) + else (0 until n).foldLeft(Option(path))((p, _) => p.flatMap(_.parent)) - } - }.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 of nth parent relatively + private def parentRelative(n: Int)(path: Path): Path = + if (n <= 0) path + else path.resolve((0 until n).map(_ => "../").mkString) + + // 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 - def getGlobalNodeModulePath: List[Path] = { - val meta = Meta.metaUrl - val req = Module.createRequire(meta) - 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)) + def getGlobalNodeModulePath: List[Path] = Try { + // hack + val globalAquaPath = parentStrict(3)(builtinPath) - // 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, - // if they were imported in aqua files - val globalNodeModulesPath = - 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 - } + // 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, + // if they were imported in aqua files + val globalNodeModulesPath = globalAquaPath.flatMap(parentStrict(3)) + 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 } + } diff --git a/js/js-imports/src/main/scala/aqua/js/Npm.scala b/js/js-imports/src/main/scala/aqua/js/Npm.scala index 12ce2ad2..e93fc021 100644 --- a/js/js-imports/src/main/scala/aqua/js/Npm.scala +++ b/js/js-imports/src/main/scala/aqua/js/Npm.scala @@ -12,14 +12,24 @@ object Meta { 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 @JSImport("module", JSImport.Namespace) object Module extends js.Object { // 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 { - def resolve(str: String): Any +object Npm { + private def require = Module.createRequire(Meta.metaUrl) + + // Resolve path to module + def resolveModule(path: String): String = require.resolve(path) }