@@ -30,7 +30,9 @@ public class NodeContext implements AutoCloseable {
30
30
private final PrintStream debugOutput ;
31
31
private final File lastJsCodeExecutionResultFile ;
32
32
private final int timeout ;
33
+ private final File parentNodeDir ;
33
34
private final File installationDir ;
35
+ private final File workingDir ;
34
36
private final File nodeExeFile ;
35
37
private final File npmExeFile ;
36
38
private final File npxExeFile ;
@@ -42,28 +44,28 @@ public NodeContext() {
42
44
}
43
45
44
46
/**
45
- * @param installationDir path of an empty directory (can also not exist yet) to install the latest Node.js into if needed.
46
- * If null Node.js will get installed into ./NodeJS-Installation ("." is the current working directory).
47
- * @param debugOutput if null, debug output won't be written/printed, otherwise it gets printed/written to the provided {@link OutputStream}.
48
- * @param timeout the max time in seconds to wait for JavaScript code to finish. Set to 0 to disable.
47
+ * @param parentNodeDir path of an empty directory (can also not exist yet) to install the latest Node.js into if needed.
48
+ * If null Node.js will get installed into ./NodeJS-Installation ("." is the current working directory).
49
+ * @param debugOutput if null, debug output won't be written/printed, otherwise it gets printed/written to the provided {@link OutputStream}.
50
+ * @param timeout the max time in seconds to wait for JavaScript code to finish. Set to 0 to disable.
49
51
*/
50
- public NodeContext (File installationDir , OutputStream debugOutput , int timeout ) {
52
+ public NodeContext (File parentNodeDir , OutputStream debugOutput , int timeout ) {
51
53
this .timeout = timeout ;
52
54
if (debugOutput == null )
53
55
this .debugOutput = new PrintStream (new TrashOutput ());
54
56
else
55
57
this .debugOutput = new PrintStream (debugOutput );
56
58
PrintStream out = this .debugOutput ;
57
- if (installationDir == null ) {
58
- this .installationDir = new File (System .getProperty ("user.dir" ) + "/NodeJS-Installation" );
59
- installationDir = this .installationDir ;
59
+ if (parentNodeDir == null ) {
60
+ this .parentNodeDir = new File (System .getProperty ("user.dir" ) + "/NodeJS-Installation" );
61
+ parentNodeDir = this .parentNodeDir ;
60
62
} else
61
- this .installationDir = installationDir ;
63
+ this .parentNodeDir = parentNodeDir ;
62
64
// Download and install NodeJS into current working directory if no installation found
63
65
try {
64
66
determineArchAndOs ();
65
- if (!installationDir .exists ()
66
- || Objects .requireNonNull (installationDir .listFiles ()).length == 0 ) {
67
+ if (!parentNodeDir .exists ()
68
+ || Objects .requireNonNull (parentNodeDir .listFiles ()).length == 0 ) {
67
69
String url = "https://nodejs.org/dist/latest/" ;
68
70
out .println ("Installing latest NodeJS release from '" + url + "'..." );
69
71
Document docLatest = Jsoup .connect (url ).get ();
@@ -82,9 +84,9 @@ public NodeContext(File installationDir, OutputStream debugOutput, int timeout)
82
84
throw new FileNotFoundException ("Failed to find latest NodeJS download url at '" + url + "' for OS '" + osType .name () + "' with ARCH '" + osArchitectureType .name () + "'." );
83
85
84
86
// Download the zip file and extract its contents
85
- if (!installationDir .exists ()) installationDir .mkdirs ();
87
+ if (!parentNodeDir .exists ()) parentNodeDir .mkdirs ();
86
88
if (osType .equals (OperatingSystemType .WINDOWS )) {
87
- File downloadZip = new File (installationDir + "/download.zip" );
89
+ File downloadZip = new File (parentNodeDir + "/download.zip" );
88
90
if (downloadZip .exists ()) downloadZip .delete ();
89
91
downloadZip .createNewFile ();
90
92
DownloadTask downloadTask = new DownloadTask ("Download" , new BetterThreadManager (), downloadUrl , downloadZip , "zip" );
@@ -95,14 +97,14 @@ public NodeContext(File installationDir, OutputStream debugOutput, int timeout)
95
97
}
96
98
out .println ("Download-Task > " + downloadTask .getStatus ());
97
99
98
- out .print ("Extracting NodeJS files..." );
100
+ out .print ("Extracting Node.js files..." );
99
101
out .flush ();
100
102
ZipFile zipFile = new ZipFile (downloadZip );
101
- zipFile .extractFile (zipFile .getFileHeaders ().get (0 ).getFileName (), installationDir .getPath ());
103
+ zipFile .extractFile (zipFile .getFileHeaders ().get (0 ).getFileName (), parentNodeDir .getPath ());
102
104
downloadZip .delete ();
103
105
out .println (" SUCCESS!" );
104
106
} else {
105
- File downloadFile = new File (installationDir + "/download.tar.gz" );
107
+ File downloadFile = new File (parentNodeDir + "/download.tar.gz" );
106
108
if (downloadFile .exists ()) downloadFile .delete ();
107
109
downloadFile .createNewFile ();
108
110
DownloadTask downloadTask = new DownloadTask ("Download" , new BetterThreadManager (), downloadUrl , downloadFile , "gzip" , "tar.gz" , "tar" , "tar+gzip" , "x-gtar" , "x-gzip" , "x-tgz" );
@@ -112,31 +114,37 @@ public NodeContext(File installationDir, OutputStream debugOutput, int timeout)
112
114
Thread .sleep (1000 );
113
115
}
114
116
out .println ("Download-Task > " + downloadTask .getStatus ());
115
- out .print ("Extracting NodeJS files..." );
117
+ out .print ("Extracting Node.js files..." );
116
118
out .flush ();
117
- ArchiverFactory .createArchiver (downloadFile ).extract (downloadFile , installationDir );
119
+ ArchiverFactory .createArchiver (downloadFile ).extract (downloadFile , parentNodeDir );
118
120
// Result should be .../headless-browser/node-js/node<version>/...
119
121
downloadFile .delete ();
120
122
out .println (" SUCCESS!" );
121
123
}
122
124
}
123
125
126
+ this .installationDir = this .parentNodeDir .listFiles ()[0 ];
127
+ Objects .requireNonNull (installationDir );
128
+ this .workingDir = new File (this .parentNodeDir .getParentFile () + "/node-js-working-dir" );
129
+ Objects .requireNonNull (workingDir );
130
+ if (!workingDir .exists ()) workingDir .mkdirs ();
131
+
124
132
if (osType .equals (OperatingSystemType .WINDOWS )) {
125
- nodeExeFile = new File (installationDir . listFiles ()[ 0 ] + "/node.exe" );
126
- npmExeFile = new File (installationDir . listFiles ()[ 0 ] + "/npm.cmd" );
127
- npxExeFile = new File (installationDir . listFiles ()[ 0 ] + "/npx.cmd" );
133
+ nodeExeFile = new File (installationDir + "/node.exe" );
134
+ npmExeFile = new File (installationDir + "/npm.cmd" );
135
+ npxExeFile = new File (installationDir + "/npx.cmd" );
128
136
} else { // Linux, mac and co.
129
- nodeExeFile = new File (installationDir . listFiles ()[ 0 ] + "/bin/node" );
130
- npmExeFile = new File (installationDir . listFiles ()[ 0 ] + "/bin/npm" );
131
- npxExeFile = new File (installationDir . listFiles ()[ 0 ] + "/bin/npx" );
137
+ nodeExeFile = new File (installationDir + "/bin/node" );
138
+ npmExeFile = new File (installationDir + "/bin/npm" );
139
+ npxExeFile = new File (installationDir + "/bin/npx" );
132
140
}
133
141
134
142
if (!nodeExeFile .exists ())
135
- throw new Exception ("node.exe couldn't be found in " + installationDir . listFiles ()[ 0 ] );
143
+ throw new Exception ("node.exe couldn't be found in " + installationDir );
136
144
if (!npmExeFile .exists ())
137
- throw new Exception ("npm.cmd couldn't be found in " + installationDir . listFiles ()[ 0 ] );
145
+ throw new Exception ("npm.cmd couldn't be found in " + installationDir );
138
146
if (!npxExeFile .exists ())
139
- throw new Exception ("npx.cmd couldn't be found in " + installationDir . listFiles ()[ 0 ] );
147
+ throw new Exception ("npx.cmd couldn't be found in " + installationDir );
140
148
} catch (Exception e ) {
141
149
System .err .println ("Error during installation of NodeJS. Details:" );
142
150
throw new RuntimeException (e );
@@ -148,6 +156,7 @@ public NodeContext(File installationDir, OutputStream debugOutput, int timeout)
148
156
out .flush ();
149
157
ProcessBuilder processBuilder = new ProcessBuilder (Arrays .asList (
150
158
nodeExeFile .getAbsolutePath (), "--interactive" ));
159
+ processBuilder .directory (workingDir );
151
160
// Must be inherited so that NodeJS closes when this application closes.
152
161
// Wrong! It seems like NodeJS closes if the parent process dies, even if its Piped I/O.
153
162
process = processBuilder .start ();
@@ -536,7 +545,7 @@ public Process executeNpmWithArgs(String... args) throws IOException, Interrupte
536
545
List <String > commands = new ArrayList <>();
537
546
commands .add (npmExeFile .getAbsolutePath ());
538
547
commands .addAll (Arrays .asList (args ));
539
- Process process = new ProcessBuilder (commands ).start ();
548
+ Process process = new ProcessBuilder (commands ).directory ( workingDir ). start ();
540
549
new AsyncInputStream (process .getInputStream ()).listeners .add (line -> debugOutput .println ("[NPM] " + line ));
541
550
new AsyncInputStream (process .getErrorStream ()).listeners .add (line -> System .err .println ("[NPM-ERROR] " + line ));
542
551
while (process .isAlive ())
@@ -549,7 +558,7 @@ public Process executeNpxWithArgs(String... args) throws IOException, Interrupte
549
558
List <String > commands = new ArrayList <>();
550
559
commands .add (npxExeFile .getAbsolutePath ());
551
560
commands .addAll (Arrays .asList (args ));
552
- Process process = new ProcessBuilder (commands ).start ();
561
+ Process process = new ProcessBuilder (commands ).directory ( workingDir ). start ();
553
562
new AsyncInputStream (process .getInputStream ()).listeners .add (line -> debugOutput .println ("[NPX] " + line ));
554
563
new AsyncInputStream (process .getErrorStream ()).listeners .add (line -> System .err .println ("[NPX-ERROR] " + line ));
555
564
while (process .isAlive ())
@@ -566,8 +575,8 @@ public NodeContext onPrintLine(Consumer<String> listener) {
566
575
return this ;
567
576
}
568
577
569
- public File getInstallationDir () {
570
- return installationDir ;
578
+ public File getParentNodeDir () {
579
+ return parentNodeDir ;
571
580
}
572
581
573
582
public File getNodeExeFile () {
@@ -610,6 +619,17 @@ public OperatingSystemType getOsType() {
610
619
return osType ;
611
620
}
612
621
622
+ public File getWorkingDir () {
623
+ return workingDir ;
624
+ }
625
+
626
+ public File getNpmExeFile () {
627
+ return npmExeFile ;
628
+ }
629
+
630
+ public File getNpxExeFile () {
631
+ return npxExeFile ;
632
+ }
613
633
614
634
public enum OperatingSystemArchitectureType {
615
635
X64 ("x64" ),
0 commit comments