@@ -3,32 +3,87 @@ package com.typesafe.sbt.jse.npm
3
3
4
4
import java .io .File
5
5
6
- import com .typesafe .sbt .jse .engines .{Engine , JsExecutionResult }
6
+ import com .typesafe .sbt .jse .engines .{Engine , JsExecutionResult , LocalEngine }
7
7
8
8
import scala .collection .immutable
9
+ import scala .collection .JavaConverters ._
9
10
import scala .collection .mutable .ListBuffer
11
+ import scala .sys .process .{Process , ProcessLogger }
12
+ import scala .util .Try
10
13
11
14
/**
12
15
* A JVM class for performing NPM commands. Requires a JS engine to use.
13
16
*/
14
- class Npm (engine : Engine , npmFile : File , verbose : Boolean = false ) {
17
+ class Npm (engine : Engine , npmFile : File , verbose : Boolean = false , preferSystemNpm : Boolean = true ) {
15
18
19
+ // TODO deprecate all constructors, file not always necessary (when using local installed)
16
20
def this (engine : Engine , npmFile : File ) = this (engine, npmFile, false )
17
21
22
+ def install (global : Boolean = false , names : Seq [String ] = Nil , outSink : String => Unit , errSink : String => Unit ): JsExecutionResult = {
23
+ val args = ListBuffer [String ]()
24
+ args += " install"
25
+ if (global) args += " -g"
26
+ if (verbose) args += " --verbose"
27
+ // args += "--no-audit" // TODO: audit as optional
28
+ args ++= names
29
+ invokeNpm(args, outSink, errSink)
30
+ }
31
+
18
32
def update (global : Boolean = false , names : Seq [String ] = Nil , outSink : String => Unit , errSink : String => Unit ): JsExecutionResult = {
19
33
val args = ListBuffer [String ]()
20
34
args += " update"
21
35
if (global) args += " -g"
22
36
if (verbose) args += " --verbose"
37
+ // args += "--no-audit" // TODO: audit as optional
23
38
args ++= names
24
39
invokeNpm(args, outSink, errSink)
25
40
}
26
41
42
+ // TODO: Also provide ci subcommand, see https://github.com/typesafehub/npm/issues/28
43
+
44
+ private def detectNpm (command : String ): Option [String ] = {
45
+ val npmExists = Try (Process (s " $command --version " ).!! ).isSuccess
46
+ if (! npmExists) {
47
+ println(s " Warning: npm detection failed. Tried the command: $command" )
48
+ None
49
+ } else {
50
+ Some (command)
51
+ }
52
+ }
53
+
27
54
private def invokeNpm (args : ListBuffer [String ], outSink : String => Unit , errSink : String => Unit ): JsExecutionResult = {
28
55
if (! engine.isNode) {
29
56
throw new IllegalStateException (" node not found: a Node.js installation is required to run npm." )
30
57
}
31
- engine.executeJs(npmFile, args.to[immutable.Seq ], Map .empty, outSink, errSink)
58
+
59
+ def executeJsNpm (): JsExecutionResult = engine.executeJs(npmFile, args.to[immutable.Seq ], Map .empty, outSink, errSink)
60
+
61
+ engine match {
62
+ case localEngine : LocalEngine if preferSystemNpm => {
63
+ // The first argument always is the command of the js engine, e.g. either just "node", "phantomjs,.. or a path like "/usr/bin/node"
64
+ // So we first try to detect a npm command in the same folder in case the user provided an explicit command that is a path
65
+ val localEngineCmd = new File (localEngine.stdArgs.head)
66
+ val localNpmCmd = if (localEngineCmd.getParent() == null ) {
67
+ // Pretty sure the command was not a path but just something like "node"
68
+ // Therefore we assume the npm command is on the path, just like the js engine command
69
+ detectNpm(" npm" )
70
+ } else {
71
+ val cmdPath = new File (localEngineCmd.getParentFile, " npm" ).getCanonicalPath
72
+ detectNpm(cmdPath).orElse(detectNpm(" npm" ))
73
+ }
74
+ localNpmCmd match {
75
+ case Some (cmd) => {
76
+ val allArgs = immutable.Seq (cmd) ++ args
77
+ val pb = new ProcessBuilder (LocalEngine .prepareArgs(allArgs).asJava)
78
+ pb.environment().putAll(localEngine.stdEnvironment.asJava)
79
+ JsExecutionResult (Process (pb).! (ProcessLogger (outSink, errSink)))
80
+ }
81
+ case None => executeJsNpm() // TODO log that fallback is happening
82
+ }
83
+ }
84
+ case _ => // e.g. Trireme provides node, but is not a local install and does not provide npm, therefore fallback using the webjar npm
85
+ executeJsNpm()
86
+ }
32
87
}
33
88
34
89
}
0 commit comments