diff --git a/sbt-scalajs-bundler/src/main/scala-sjs-0.6/scalajsbundler/sbtplugin/Settings.scala b/sbt-scalajs-bundler/src/main/scala-sjs-0.6/scalajsbundler/sbtplugin/Settings.scala index 82f8afc7..2eec2e25 100644 --- a/sbt-scalajs-bundler/src/main/scala-sjs-0.6/scalajsbundler/sbtplugin/Settings.scala +++ b/sbt-scalajs-bundler/src/main/scala-sjs-0.6/scalajsbundler/sbtplugin/Settings.scala @@ -10,7 +10,7 @@ import org.scalajs.sbtplugin.ScalaJSPlugin.AutoImport.{fastOptJS, jsEnv, loadedJ import org.scalajs.sbtplugin.ScalaJSPluginInternal.{scalaJSEnsureUnforked, scalaJSModuleIdentifier} import sbt.Keys.{loadedTestFrameworks, streams, testFrameworks, version} import sbt._ -import scalajsbundler.sbtplugin.ScalaJSBundlerPlugin.autoImport.{installJsdom, npmUpdate, requireJsDomEnv, webpack, webpackConfigFile, webpackNodeArgs, webpackResources} +import scalajsbundler.sbtplugin.ScalaJSBundlerPlugin.autoImport.{installJsdom, npmUpdate, requireJsDomEnv, webpack, webpackConfigFile, webpackNodeEnvVars, webpackNodeArgs, webpackResources} import scalajsbundler.sbtplugin.ScalaJSBundlerPlugin.{createdTestAdapters, ensureModuleKindIsCommonJSModule} import scalajsbundler.scalajs.compat.testing.TestAdapter import scalajsbundler.{JSDOMNodeJSEnv, JsDomTestEntries, NpmPackage, Webpack} @@ -51,6 +51,7 @@ private[sbtplugin] object Settings { val webpackVersion = (version in webpack).value val customWebpackConfigFile = (webpackConfigFile in Test).value + val nodeEnvVars = (webpackNodeEnvVars in Test).value.toSeq val nodeArgs = (webpackNodeArgs in Test).value val writeTestBundleFunction = @@ -68,17 +69,17 @@ private[sbtplugin] object Settings { NpmPackage(webpackVersion).major match { case Some(4) => // TODO: It assumes tests are run on development mode. It should instead use build settings - Webpack.run(nodeArgs: _*)("--mode", "development", "--config", customConfigFileCopy.getAbsolutePath, loader.absolutePath, "--output", bundle.absolutePath)(targetDir, logger) + Webpack.run(nodeEnvVars: _*)(nodeArgs: _*)("--mode", "development", "--config", customConfigFileCopy.getAbsolutePath, loader.absolutePath, "--output", bundle.absolutePath)(targetDir, logger) case _ => - Webpack.run(nodeArgs: _*)("--config", customConfigFileCopy.getAbsolutePath, loader.absolutePath, bundle.absolutePath)(targetDir, logger) + Webpack.run(nodeEnvVars: _*)(nodeArgs: _*)("--config", customConfigFileCopy.getAbsolutePath, loader.absolutePath, bundle.absolutePath)(targetDir, logger) } case None => NpmPackage(webpackVersion).major match { case Some(4) => // TODO: It assumes tests are run on development mode. It should instead use build settings - Webpack.run(nodeArgs: _*)("--mode", "development", loader.absolutePath, "--output", bundle.absolutePath)(targetDir, logger) + Webpack.run(nodeEnvVars: _*)(nodeArgs: _*)("--mode", "development", loader.absolutePath, "--output", bundle.absolutePath)(targetDir, logger) case _ => - Webpack.run(nodeArgs: _*)(loader.absolutePath, bundle.absolutePath)(targetDir, logger) + Webpack.run(nodeEnvVars: _*)(nodeArgs: _*)(loader.absolutePath, bundle.absolutePath)(targetDir, logger) } } diff --git a/sbt-scalajs-bundler/src/main/scala-sjs-1.x/scalajsbundler/sbtplugin/Settings.scala b/sbt-scalajs-bundler/src/main/scala-sjs-1.x/scalajsbundler/sbtplugin/Settings.scala index c972f2ff..6c5b5c8e 100644 --- a/sbt-scalajs-bundler/src/main/scala-sjs-1.x/scalajsbundler/sbtplugin/Settings.scala +++ b/sbt-scalajs-bundler/src/main/scala-sjs-1.x/scalajsbundler/sbtplugin/Settings.scala @@ -11,7 +11,7 @@ import sbt.Keys.{configuration, fork, loadedTestFrameworks, streams, testFramewo import sbt._ import sbt.testing.Framework import scalajsbundler.{JSDOMNodeJSEnv, Webpack, JsDomTestEntries, NpmPackage} -import scalajsbundler.sbtplugin.ScalaJSBundlerPlugin.autoImport.{installJsdom, npmUpdate, requireJsDomEnv, webpackConfigFile, webpackNodeArgs, webpackResources, webpack} +import scalajsbundler.sbtplugin.ScalaJSBundlerPlugin.autoImport.{installJsdom, npmUpdate, requireJsDomEnv, webpackConfigFile, webpackNodeEnvVars, webpackNodeArgs, webpackResources, webpack} import scalajsbundler.sbtplugin.ScalaJSBundlerPlugin.{createdTestAdapters, ensureModuleKindIsCommonJSModule} import scalajsbundler.scalajs.compat.io.FileVirtualBinaryFile import scalajsbundler.scalajs.compat.testing.TestAdapter @@ -38,6 +38,7 @@ private[sbtplugin] object Settings { val webpackVersion = (version in webpack).value val customWebpackConfigFile = (webpackConfigFile in Test).value + val nodeEnvVars = (webpackNodeEnvVars in Test).value.toSeq val nodeArgs = (webpackNodeArgs in Test).value val writeTestBundleFunction = @@ -55,17 +56,17 @@ private[sbtplugin] object Settings { NpmPackage(webpackVersion).major match { case Some(4) => // TODO: It assumes tests are run on development mode. It should instead use build settings - Webpack.run(nodeArgs: _*)("--mode", "development", "--config", customConfigFileCopy.getAbsolutePath, loader.absolutePath, "--output", bundle.absolutePath)(targetDir, logger) + Webpack.run(nodeEnvVars: _*)(nodeArgs: _*)("--mode", "development", "--config", customConfigFileCopy.getAbsolutePath, loader.absolutePath, "--output", bundle.absolutePath)(targetDir, logger) case _ => - Webpack.run(nodeArgs: _*)("--config", customConfigFileCopy.getAbsolutePath, loader.absolutePath, bundle.absolutePath)(targetDir, logger) + Webpack.run(nodeEnvVars: _*)(nodeArgs: _*)("--config", customConfigFileCopy.getAbsolutePath, loader.absolutePath, bundle.absolutePath)(targetDir, logger) } case None => NpmPackage(webpackVersion).major match { case Some(4) => // TODO: It assumes tests are run on development mode. It should instead use build settings - Webpack.run(nodeArgs: _*)("--mode", "development", loader.absolutePath, "--output", bundle.absolutePath)(targetDir, logger) + Webpack.run(nodeEnvVars: _*)(nodeArgs: _*)("--mode", "development", loader.absolutePath, "--output", bundle.absolutePath)(targetDir, logger) case _ => - Webpack.run(nodeArgs: _*)(loader.absolutePath, bundle.absolutePath)(targetDir, logger) + Webpack.run(nodeEnvVars: _*)(nodeArgs: _*)(loader.absolutePath, bundle.absolutePath)(targetDir, logger) } } diff --git a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/Webpack.scala b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/Webpack.scala index 459e398d..4a3759ae 100644 --- a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/Webpack.scala +++ b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/Webpack.scala @@ -167,6 +167,8 @@ object Webpack { * @param entry Scala.js application to bundle * @param targetDir Target directory (and working directory for Nodejs) * @param extraArgs Extra arguments passed to webpack + * @param nodeEnvVars Extra environment variables to node.js + * @param nodeArgs Extra arguments to node.js * @param mode Mode for webpack 4 * @param log Logger * @return The generated bundles @@ -179,6 +181,7 @@ object Webpack { entry: BundlerFile.Application, targetDir: File, extraArgs: Seq[String], + nodeEnvVars: Map[String, String], nodeArgs: Seq[String], mode: WebpackMode, log: Logger @@ -191,7 +194,7 @@ object Webpack { log.info("Bundling the application with its NPM dependencies") val args = extraArgs ++: Seq("--config", configFile.absolutePath) - val stats = Webpack.run(nodeArgs: _*)(args: _*)(targetDir, log) + val stats = Webpack.run(nodeEnvVars.toSeq: _*)(nodeArgs: _*)(args: _*)(targetDir, log) stats.foreach(_.print(log)) // Attempt to discover the actual name produced by webpack indexing by chunk name and discarding maps @@ -211,6 +214,8 @@ object Webpack { * @param entryPointFile The entrypoint file to bundle dependencies for * @param libraryModuleName The library module name to assign the webpack bundle to * @param extraArgs Extra arguments passed to webpack + * @param nodeEnvVars Extra environment variables to node.js + * @param nodeArgs Extra arguments to node.js * @param mode Mode for webpack 4 * @param log Logger * @return The generated bundle @@ -223,6 +228,7 @@ object Webpack { entryPointFile: BundlerFile.EntryPoint, libraryModuleName: String, extraArgs: Seq[String], + nodeEnvVars: Map[String, String], nodeArgs: Seq[String], mode: WebpackMode, log: Logger @@ -241,7 +247,7 @@ object Webpack { .getOrElse(generatedWebpackConfigFile.file) val args = extraArgs ++: Seq("--config", configFile.absolutePath) - val stats = Webpack.run(nodeArgs: _*)(args: _*)(generatedWebpackConfigFile.targetDir.toFile, log) + val stats = Webpack.run(nodeEnvVars.toSeq: _*)(nodeArgs: _*)(args: _*)(generatedWebpackConfigFile.targetDir.toFile, log) stats.foreach(_.print(log)) val library = generatedWebpackConfigFile.asLibrary(stats) @@ -288,16 +294,17 @@ object Webpack { /** * Runs the webpack command. * + * @param nodeEnvVars Extra environment variables to node.js * @param nodeArgs node.js cli flags * @param args Arguments to pass to the webpack command * @param workingDir Working directory in which the Nodejs will be run (where there is the `node_modules` subdirectory) * @param log Logger */ - def run(nodeArgs: String*)(args: String*)(workingDir: File, log: Logger): Option[WebpackStats] = { + def run(nodeEnvVars: (String, String)*)(nodeArgs: String*)(args: String*)(workingDir: File, log: Logger): Option[WebpackStats] = { val webpackBin = workingDir / "node_modules" / "webpack" / "bin" / "webpack" val params = nodeArgs ++ Seq(webpackBin.absolutePath, "--bail", "--profile", "--json") ++ args val cmd = "node" +: params - Commands.run(cmd, workingDir, log, jsonOutput(cmd, log)).fold(sys.error, _.flatten) + Commands.run(cmd, workingDir, nodeEnvVars.toMap, log, jsonOutput(cmd, log)).fold(sys.error, _.flatten) } } diff --git a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/LibraryTasks.scala b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/LibraryTasks.scala index 606b7c0d..8b40d39f 100644 --- a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/LibraryTasks.scala +++ b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/LibraryTasks.scala @@ -76,6 +76,7 @@ object LibraryTasks { webpackResourceFiles ++ compileResources val cacheLocation = streams.value.cacheDirectory / s"${stage.key.label}-webpack-libraries" val extraArgs = (webpackExtraArgs in stage).value + val nodeEnvVars = (webpackNodeEnvVars in stage).value val nodeArgs = (webpackNodeArgs in stage).value val webpackMode = Webpack.WebpackMode((scalaJSLinkerConfig in stage).value) @@ -94,6 +95,7 @@ object LibraryTasks { entryPointFile, mode.exportedName, extraArgs, + nodeEnvVars, nodeArgs, webpackMode, log diff --git a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/ScalaJSBundlerPlugin.scala b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/ScalaJSBundlerPlugin.scala index ace7f711..80e8d405 100644 --- a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/ScalaJSBundlerPlugin.scala +++ b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/ScalaJSBundlerPlugin.scala @@ -385,6 +385,18 @@ object ScalaJSBundlerPlugin extends AutoPlugin { "Custom arguments to webpack" ) + /** + * Custom environment variables to node.js when running webpack tasks + * + * Defaults to an empty map. + * + * @group settings + */ + val webpackNodeEnvVars = SettingKey[Map[String, String]]( + "webpackNodeEnvVars", + "Custom environment variables to node.js when running webpack tasks" + ) + /** * node.js cli custom arguments as described in https://nodejs.org/api/cli.html * @@ -580,6 +592,7 @@ object ScalaJSBundlerPlugin extends AutoPlugin { webpackMonitoredDirectories := Seq(), (includeFilter in webpackMonitoredFiles) := AllPassFilter, webpackExtraArgs := Seq.empty, + webpackNodeEnvVars := Map.empty, webpackNodeArgs := Seq.empty, npmExtraArgs := Seq.empty, diff --git a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/WebpackTasks.scala b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/WebpackTasks.scala index 8997d4e1..394e8bf7 100644 --- a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/WebpackTasks.scala +++ b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/WebpackTasks.scala @@ -30,6 +30,7 @@ object WebpackTasks { val log = streams.value.log val monitoredFiles = (webpackMonitoredFiles in stage).value val extraArgs = (webpackExtraArgs in stage).value + val nodeEnvVars = (webpackNodeEnvVars in stage).value val nodeArgs = (webpackNodeArgs in stage).value val webpackMode = Webpack.WebpackMode((scalaJSLinkerConfig in stage).value) @@ -46,6 +47,7 @@ object WebpackTasks { entriesList, targetDir, extraArgs, + nodeEnvVars, nodeArgs, webpackMode, log diff --git a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/util/Commands.scala b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/util/Commands.scala index 81edd57b..a4faac6f 100644 --- a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/util/Commands.scala +++ b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/util/Commands.scala @@ -8,7 +8,7 @@ import scala.sys.process.ProcessLogger object Commands { - def run[A](cmd: Seq[String], cwd: File, logger: Logger, outputProcess: InputStream => A): Either[String, Option[A]] = { + def run[A](cmd: Seq[String], cwd: File, extraEnv: Map[String, String], logger: Logger, outputProcess: InputStream => A): Either[String, Option[A]] = { val toErrorLog = (is: InputStream) => { scala.io.Source.fromInputStream(is).getLines.foreach(msg => logger.error(msg)) is.close() @@ -23,7 +23,7 @@ object Commands { } logger.debug(s"Command: ${cmd.mkString(" ")}") - val process = Process(cmd, cwd) + val process = Process(cmd, cwd, extraEnv.toSeq: _*) val processIO = BasicIO.standard(false).withOutput(outputCapture).withError(toErrorLog) val code: Int = process.run(processIO).exitValue() if (code != 0) { @@ -35,7 +35,7 @@ object Commands { def run(cmd: Seq[String], cwd: File, logger: Logger): Unit = { val toInfoLog = (is: InputStream) => scala.io.Source.fromInputStream(is).getLines.foreach(msg => logger.info(msg)) - run(cmd, cwd, logger, toInfoLog).fold(sys.error, _ => ()) + run(cmd, cwd, Map.empty, logger, toInfoLog).fold(sys.error, _ => ()) } def start(cmd: Seq[String], cwd: File, logger: Logger): Process =