@@ -34,25 +34,36 @@ final class NodeJSEnv(config: NodeJSEnv.Config) extends JSEnv {
34
34
35
35
def start (input : Input , runConfig : RunConfig ): JSRun = {
36
36
NodeJSEnv .validator.validate(runConfig)
37
- internalStart(initFiles ++ inputFiles(input), runConfig)
37
+ validateInput(input)
38
+ internalStart(initFiles, input, runConfig)
38
39
}
39
40
40
41
def startWithCom (input : Input , runConfig : RunConfig ,
41
42
onMessage : String => Unit ): JSComRun = {
42
43
NodeJSEnv .validator.validate(runConfig)
44
+ validateInput(input)
43
45
ComRun .start(runConfig, onMessage) { comLoader =>
44
- val files = initFiles ::: (comLoader :: inputFiles(input))
45
- internalStart(files, runConfig)
46
+ internalStart(initFiles :+ comLoader, input, runConfig)
46
47
}
47
48
}
48
49
49
- private def internalStart (files : List [VirtualBinaryFile ],
50
+ private def validateInput (input : Input ): Unit = {
51
+ input match {
52
+ case _:Input .ScriptsToLoad | _:Input .CommonJSModulesToLoad =>
53
+ // ok
54
+ case _ =>
55
+ throw new UnsupportedInputException (input)
56
+ }
57
+ }
58
+
59
+ private def internalStart (initFiles : List [VirtualBinaryFile ], input : Input ,
50
60
runConfig : RunConfig ): JSRun = {
51
61
val command = config.executable :: config.args
52
62
val externalConfig = ExternalJSRun .Config ()
53
63
.withEnv(env)
54
64
.withRunConfig(runConfig)
55
- ExternalJSRun .start(command, externalConfig)(NodeJSEnv .write(files))
65
+ ExternalJSRun .start(command, externalConfig)(
66
+ NodeJSEnv .write(initFiles, input))
56
67
}
57
68
58
69
private def initFiles : List [VirtualBinaryFile ] = {
@@ -103,39 +114,112 @@ object NodeJSEnv {
103
114
)
104
115
}
105
116
106
- private def write (files : List [VirtualBinaryFile ])(out : OutputStream ): Unit = {
117
+ private def write (initFiles : List [VirtualBinaryFile ], input : Input )(
118
+ out : OutputStream ): Unit = {
107
119
val p = new PrintStream (out, false , " UTF8" )
108
120
try {
109
- files.foreach {
110
- case file : FileVirtualBinaryFile =>
111
- val fname = file.file.getAbsolutePath
112
- p.println(s """ require(" ${escapeJS(fname)}"); """ )
113
- case f =>
114
- val in = f.inputStream
115
- try {
116
- val buf = new Array [Byte ](4096 )
117
-
118
- @ tailrec
119
- def loop (): Unit = {
120
- val read = in.read(buf)
121
- if (read != - 1 ) {
122
- p.write(buf, 0 , read)
123
- loop()
124
- }
125
- }
126
-
127
- loop()
128
- } finally {
129
- in.close()
130
- }
131
-
132
- p.println()
121
+ def writeRunScript (file : VirtualBinaryFile ): Unit = {
122
+ file match {
123
+ case file : FileVirtualBinaryFile =>
124
+ val pathJS = " \" " + escapeJS(file.file.getAbsolutePath) + " \" "
125
+ p.println(s """
126
+ require('vm').runInThisContext(
127
+ require('fs').readFileSync( $pathJS, { encoding: "utf-8" }),
128
+ { filename: $pathJS, displayErrors: true }
129
+ );
130
+ """ )
131
+
132
+ case _ =>
133
+ val code = readInputStreamToString(file.inputStream)
134
+ val codeJS = " \" " + escapeJS(code) + " \" "
135
+ val pathJS = " \" " + escapeJS(file.path) + " \" "
136
+ p.println(s """
137
+ require('vm').runInThisContext(
138
+ $codeJS,
139
+ { filename: $pathJS, displayErrors: true }
140
+ );
141
+ """ )
142
+ }
143
+ }
144
+
145
+ def writeRequire (file : VirtualBinaryFile ): Unit = {
146
+ file match {
147
+ case file : FileVirtualBinaryFile =>
148
+ p.println(s """ require(" ${escapeJS(file.file.getAbsolutePath)}") """ )
149
+
150
+ case _ =>
151
+ val f = tmpFile(file.path, file.inputStream)
152
+ p.println(s """ require(" ${escapeJS(f.getAbsolutePath)}") """ )
153
+ }
154
+ }
155
+
156
+ for (initFile <- initFiles)
157
+ writeRunScript(initFile)
158
+
159
+ input match {
160
+ case Input .ScriptsToLoad (scripts) =>
161
+ for (script <- scripts)
162
+ writeRunScript(script)
163
+
164
+ case Input .CommonJSModulesToLoad (modules) =>
165
+ for (module <- modules)
166
+ writeRequire(module)
133
167
}
134
168
} finally {
135
169
p.close()
136
170
}
137
171
}
138
172
173
+ private def readInputStreamToString (inputStream : InputStream ): String = {
174
+ val baos = new java.io.ByteArrayOutputStream
175
+ val in = inputStream
176
+ try {
177
+ val buf = new Array [Byte ](4096 )
178
+
179
+ @ tailrec
180
+ def loop (): Unit = {
181
+ val read = in.read(buf)
182
+ if (read != - 1 ) {
183
+ baos.write(buf, 0 , read)
184
+ loop()
185
+ }
186
+ }
187
+
188
+ loop()
189
+ } finally {
190
+ in.close()
191
+ }
192
+ new String (baos.toByteArray(), StandardCharsets .UTF_8 )
193
+ }
194
+
195
+ private def tmpFile (path : String , content : InputStream ): File = {
196
+ import java .nio .file .{Files , StandardCopyOption }
197
+
198
+ try {
199
+ val f = createTmpFile(path)
200
+ Files .copy(content, f.toPath(), StandardCopyOption .REPLACE_EXISTING )
201
+ f
202
+ } finally {
203
+ content.close()
204
+ }
205
+ }
206
+
207
+ // tmpSuffixRE and createTmpFile copied from HTMLRunnerBuilder.scala
208
+
209
+ private val tmpSuffixRE = """ [a-zA-Z0-9-_.]*$""" .r
210
+
211
+ private def createTmpFile (path : String ): File = {
212
+ /* - createTempFile requires a prefix of at least 3 chars
213
+ * - we use a safe part of the path as suffix so the extension stays (some
214
+ * browsers need that) and there is a clue which file it came from.
215
+ */
216
+ val suffix = tmpSuffixRE.findFirstIn(path).orNull
217
+
218
+ val f = File .createTempFile(" tmp-" , suffix)
219
+ f.deleteOnExit()
220
+ f
221
+ }
222
+
139
223
/** Requirements for source map support. */
140
224
sealed abstract class SourceMap
141
225
0 commit comments