@@ -33,49 +33,40 @@ final class NodeJSEnv(config: NodeJSEnv.Config) extends JSEnv {
3333
3434 val name : String = " Node.js"
3535
36- def start (input : Input , runConfig : RunConfig ): JSRun = {
36+ def start (input : Seq [ Input ] , runConfig : RunConfig ): JSRun = {
3737 NodeJSEnv .validator.validate(runConfig)
3838 validateInput(input)
39- internalStart(initFiles, input, runConfig)
39+ internalStart(initFiles ++ input, runConfig)
4040 }
4141
42- def startWithCom (input : Input , runConfig : RunConfig ,
42+ def startWithCom (input : Seq [ Input ] , runConfig : RunConfig ,
4343 onMessage : String => Unit ): JSComRun = {
4444 NodeJSEnv .validator.validate(runConfig)
4545 validateInput(input)
4646 ComRun .start(runConfig, onMessage) { comLoader =>
47- internalStart(initFiles :+ comLoader, input, runConfig)
47+ internalStart(initFiles ++ ( Input . Script ( comLoader) +: input) , runConfig)
4848 }
4949 }
5050
51- private def validateInput (input : Input ): Unit = {
52- input match {
53- case _:Input .ScriptsToLoad | _:Input .ESModulesToLoad |
54- _:Input .CommonJSModulesToLoad =>
55- // ok
56- case _ =>
57- throw new UnsupportedInputException (input)
58- }
51+ private def validateInput (input : Seq [Input ]): Unit = input.foreach {
52+ case _:Input .Script | _:Input .ESModule | _:Input .CommonJSModule =>
53+ // ok
54+ case _ =>
55+ throw new UnsupportedInputException (input)
5956 }
6057
61- private def internalStart (initFiles : List [ Path ], input : Input , runConfig : RunConfig ): JSRun = {
58+ private def internalStart (input : Seq [ Input ] , runConfig : RunConfig ): JSRun = {
6259 val command = config.executable :: config.args
6360 val externalConfig = ExternalJSRun .Config ()
6461 .withEnv(env)
6562 .withRunConfig(runConfig)
66- ExternalJSRun .start(command, externalConfig)(
67- NodeJSEnv .write(initFiles, input))
63+ ExternalJSRun .start(command, externalConfig)(NodeJSEnv .write(input))
6864 }
6965
70- private def initFiles : List [ Path ] = config.sourceMap match {
66+ private def initFiles : Seq [ Input ] = config.sourceMap match {
7167 case SourceMap .Disable => Nil
72- case SourceMap .EnableIfAvailable => installSourceMapIfAvailable :: Nil
73- case SourceMap .Enable => installSourceMap :: Nil
74- }
75-
76- private def inputFiles (input : Input ) = input match {
77- case Input .ScriptsToLoad (scripts) => scripts
78- case _ => throw new UnsupportedInputException (input)
68+ case SourceMap .EnableIfAvailable => Input .Script (installSourceMapIfAvailable) :: Nil
69+ case SourceMap .Enable => Input .Script (installSourceMap) :: Nil
7970 }
8071
8172 private def env : Map [String , String ] =
@@ -104,68 +95,70 @@ object NodeJSEnv {
10495 " require('source-map-support').install();" .getBytes(StandardCharsets .UTF_8 ))
10596 }
10697
107- private def write (initFiles : List [Path ], input : Input )(out : OutputStream ): Unit = {
108- val p = new PrintStream (out, false , " UTF8" )
109- try {
110- def writeRunScript (path : Path ): Unit = {
111- try {
112- val f = path.toFile
113- val pathJS = " \" " + escapeJS(f.getAbsolutePath) + " \" "
114- p.println(s """
98+ private def write (input : Seq [Input ])(out : OutputStream ): Unit = {
99+ def runScript (path : Path ): String = {
100+ try {
101+ val f = path.toFile
102+ val pathJS = " \" " + escapeJS(f.getAbsolutePath) + " \" "
103+ s """
104+ require('vm').runInThisContext(
105+ require('fs').readFileSync( $pathJS, { encoding: "utf-8" }),
106+ { filename: $pathJS, displayErrors: true }
107+ )
108+ """
109+ } catch {
110+ case _ : UnsupportedOperationException =>
111+ val code = new String (Files .readAllBytes(path), StandardCharsets .UTF_8 )
112+ val codeJS = " \" " + escapeJS(code) + " \" "
113+ val pathJS = " \" " + escapeJS(path.toString) + " \" "
114+ s """
115115 require('vm').runInThisContext(
116- require('fs').readFileSync( $pathJS , { encoding: "utf-8" }) ,
116+ $codeJS ,
117117 { filename: $pathJS, displayErrors: true }
118- );
119- """ )
120- } catch {
121- case _ : UnsupportedOperationException =>
122- val code = new String (Files .readAllBytes(path), StandardCharsets .UTF_8 )
123- val codeJS = " \" " + escapeJS(code) + " \" "
124- val pathJS = " \" " + escapeJS(path.toString) + " \" "
125- p.println(s """
126- require('vm').runInThisContext(
127- $codeJS,
128- { filename: $pathJS, displayErrors: true }
129- );
130- """ )
131- }
118+ )
119+ """
132120 }
121+ }
133122
134- for (initFile <- initFiles)
135- writeRunScript(initFile)
136-
137- input match {
138- case Input .ScriptsToLoad (scripts) =>
139- for (script <- scripts)
140- writeRunScript(script)
141-
142- case Input .CommonJSModulesToLoad (modules) =>
143- for (module <- modules)
144- p.println(s """ require(" ${escapeJS(toFile(module).getAbsolutePath)}") """ )
145-
146- case Input .ESModulesToLoad (modules) =>
147- if (modules.nonEmpty) {
148- val uris = modules.map(m => toFile(m).toURI)
149-
150- val imports = uris.map { uri =>
151- s """ import(" ${escapeJS(uri.toASCIIString)}") """
152- }
153- val importChain = imports.reduceLeft { (prev, imprt) =>
154- s """ $prev.then(_ => $imprt) """
155- }
156-
157- val importerFileContent = {
158- s """
159- | $importChain.catch(e => {
160- | console.error(e);
161- | process.exit(1);
162- |});
163- """ .stripMargin
164- }
165- val f = createTmpFile(" importer.js" )
166- Files .write(f.toPath, importerFileContent.getBytes(StandardCharsets .UTF_8 ))
167- p.println(s """ require(" ${escapeJS(f.getAbsolutePath)}"); """ )
168- }
123+ def requireCommonJSModule (module : Path ): String =
124+ s """ require(" ${escapeJS(toFile(module).getAbsolutePath)}") """
125+
126+ def importESModule (module : Path ): String =
127+ s """ import(" ${escapeJS(toFile(module).toURI.toASCIIString)}") """
128+
129+ def execInputExpr (input : Input ): String = input match {
130+ case Input .Script (script) => runScript(script)
131+ case Input .CommonJSModule (module) => requireCommonJSModule(module)
132+ case Input .ESModule (module) => importESModule(module)
133+ }
134+
135+ val p = new PrintStream (out, false , " UTF8" )
136+ try {
137+ if (! input.exists(_.isInstanceOf [Input .ESModule ])) {
138+ /* If there is no ES module in the input, we can do everything
139+ * synchronously, and directly on the standard input.
140+ */
141+ for (item <- input)
142+ p.println(execInputExpr(item) + " ;" )
143+ } else {
144+ /* If there is at least one ES module, we must asynchronous chain things,
145+ * and we must use an actual file to feed code to Node.js (because
146+ * `import()` cannot be used from the standard input).
147+ */
148+ val importChain = input.foldLeft(" Promise.resolve()" ) { (prev, item) =>
149+ s " $prev. \n then( ${execInputExpr(item)}) "
150+ }
151+ val importerFileContent = {
152+ s """
153+ | $importChain.catch(e => {
154+ | console.error(e);
155+ | process.exit(1);
156+ |});
157+ """ .stripMargin
158+ }
159+ val f = createTmpFile(" importer.js" )
160+ Files .write(f.toPath, importerFileContent.getBytes(StandardCharsets .UTF_8 ))
161+ p.println(s """ require(" ${escapeJS(f.getAbsolutePath)}"); """ )
169162 }
170163 } finally {
171164 p.close()
0 commit comments