@@ -34,25 +34,36 @@ final class NodeJSEnv(config: NodeJSEnv.Config) extends JSEnv {
3434
3535 def start (input : Input , runConfig : RunConfig ): JSRun = {
3636 NodeJSEnv .validator.validate(runConfig)
37- internalStart(initFiles ++ inputFiles(input), runConfig)
37+ validateInput(input)
38+ internalStart(initFiles, input, runConfig)
3839 }
3940
4041 def startWithCom (input : Input , runConfig : RunConfig ,
4142 onMessage : String => Unit ): JSComRun = {
4243 NodeJSEnv .validator.validate(runConfig)
44+ validateInput(input)
4345 ComRun .start(runConfig, onMessage) { comLoader =>
44- val files = initFiles ::: (comLoader :: inputFiles(input))
45- internalStart(files, runConfig)
46+ internalStart(initFiles :+ comLoader, input, runConfig)
4647 }
4748 }
4849
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 ,
5060 runConfig : RunConfig ): JSRun = {
5161 val command = config.executable :: config.args
5262 val externalConfig = ExternalJSRun .Config ()
5363 .withEnv(env)
5464 .withRunConfig(runConfig)
55- ExternalJSRun .start(command, externalConfig)(NodeJSEnv .write(files))
65+ ExternalJSRun .start(command, externalConfig)(
66+ NodeJSEnv .write(initFiles, input))
5667 }
5768
5869 private def initFiles : List [VirtualBinaryFile ] = {
@@ -103,39 +114,112 @@ object NodeJSEnv {
103114 )
104115 }
105116
106- private def write (files : List [VirtualBinaryFile ])(out : OutputStream ): Unit = {
117+ private def write (initFiles : List [VirtualBinaryFile ], input : Input )(
118+ out : OutputStream ): Unit = {
107119 val p = new PrintStream (out, false , " UTF8" )
108120 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)
133167 }
134168 } finally {
135169 p.close()
136170 }
137171 }
138172
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+
139223 /** Requirements for source map support. */
140224 sealed abstract class SourceMap
141225
0 commit comments