sbt和scala.js(带Node.js)可以';由于“";TypeError:undefined不是函数;

sbt和scala.js(带Node.js)可以';由于“";TypeError:undefined不是函数;,sbt,scala.js,Sbt,Scala.js,当我使用sbt、scala.js(Node.js上的一段本地javascript代码)运行时,我需要一个错误的帮助 [info] Running net.walend.graph.results.PlotTime Hello from scala [error] /Users/dwalend/projects/ScalaGraphMinimizer/toGhPages/target/scala-2.11/toghpages-fastopt.js:1854 [error] $g["hello"

当我使用sbt、scala.js(Node.js上的一段本地javascript代码)运行时,我需要一个错误的帮助

[info] Running net.walend.graph.results.PlotTime
Hello from scala
[error] /Users/dwalend/projects/ScalaGraphMinimizer/toGhPages/target/scala-2.11/toghpages-fastopt.js:1854
[error]   $g["hello"]();
[error]              ^
[error] TypeError: undefined is not a function
[error]     at $c_Lnet_walend_graph_results_PlotTime$.main__V (/Users/dwalend/projects/ScalaGraphMinimizer/toGhPages/target/scala-2.11/toghpages-fastopt.js:1854:14)
[error]     at $c_Lnet_walend_graph_results_PlotTime$.$$js$exported$meth$main__O (/Users/dwalend/projects/ScalaGraphMinimizer/toGhPages/target/scala-2.11/toghpages-fastopt.js:1861:8)
[error]     at $c_Lnet_walend_graph_results_PlotTime$.main (/Users/dwalend/projects/ScalaGraphMinimizer/toGhPages/target/scala-2.11/toghpages-fastopt.js:1864:15)
[error]     at Object.<anonymous> (/Users/dwalend/projects/ScalaGraphMinimizer/toGhPages/target/scala-2.11/toghpages-launcher.js:2:107)
[error]     at Module._compile (module.js:460:26)
[error]     at Object.Module._extensions..js (module.js:478:10)
[error]     at Module.load (module.js:355:32)
[error]     at Function.Module._load (module.js:310:12)
[error]     at Module.require (module.js:365:17)
[error]     at require (module.js:384:17)
org.scalajs.jsenv.ExternalJSEnv$NonZeroExitException: node.js exited with code 1
      at org.scalajs.jsenv.ExternalJSEnv$AbstractExtRunner.waitForVM(ExternalJSEnv.scala:96)
      at org.scalajs.jsenv.ExternalJSEnv$ExtRunner.run(ExternalJSEnv.scala:143)
      at org.scalajs.sbtplugin.ScalaJSPluginInternal$.org$scalajs$sbtplugin$ScalaJSPluginInternal$$jsRun(ScalaJSPluginInternal.scala:479)
      at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$45$$anonfun$apply$27$$anonfun$apply$28.apply(ScalaJSPluginInternal.scala:539)
      at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$45$$anonfun$apply$27$$anonfun$apply$28.apply(ScalaJSPluginInternal.scala:533)
      at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
我甚至无法通过Node.js从algorthmTime.js中获得“hello”

function hello() {
  console.log("hello from js")
}
Scala pretty trim中的main():

对象PlotTime扩展了js.JSApp{

def main(): Unit = {
  println("Hello from scala")
  global.hello()

  val png = global.dataToPng("benchmark/results/v0.1.2/dijkstra.csv")
  println(png)
}
}

在尝试Node.js之前,我进一步使用了phantom.js和Rhino。sbt run进入我的本地javascript代码,并在d3中暂停

[info] Running net.walend.graph.results.PlotTime
Hello from scala
hello from js
org.mozilla.javascript.EcmaError: TypeError: Cannot call method "querySelector" of undefined (/Users/dwalend/.ivy2/cache/org.webjars/d3js/jars/d3js-3.5.5-1.jar#META-INF/resources/webjars/d3js/3.5.5/d3.min.js#3)
  at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:3701)
  at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:3679)
  at org.mozilla.javascript.ScriptRuntime.typeError(ScriptRuntime.java:3707)
  at org.mozilla.javascript.ScriptRuntime.typeError2(ScriptRuntime.java:3726)
  at org.mozilla.javascript.ScriptRuntime.undefCallError(ScriptRuntime.java:3743)
  at org.mozilla.javascript.ScriptRuntime.getPropFunctionAndThisHelper(ScriptRuntime.java:2269)
  at org.mozilla.javascript.ScriptRuntime.getPropFunctionAndThis(ScriptRuntime.java:2262)
  at org.mozilla.javascript.Interpreter.interpretLoop(Interpreter.java:1317)
  at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:815)
  at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:109)
  at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:394)
  at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3102)
  at org.mozilla.javascript.InterpretedFunction.exec(InterpretedFunction.java:120)
  at org.mozilla.javascript.Context.evaluateString(Context.java:1078)
  at org.scalajs.jsenv.rhino.package$ContextOps$.evaluateFile$extension(package.scala:21)
  at org.scalajs.jsenv.rhino.RhinoJSEnv.org$scalajs$jsenv$rhino$RhinoJSEnv$$internalRunJS(RhinoJSEnv.scala:157)
  at org.scalajs.jsenv.rhino.RhinoJSEnv$Runner.run(RhinoJSEnv.scala:62)
  at org.scalajs.sbtplugin.ScalaJSPluginInternal$.org$scalajs$sbtplugin$ScalaJSPluginInternal$$jsRun(ScalaJSPluginInternal.scala:479)
  at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$45$$anonfun$apply$27$$anonfun$apply$28.apply(ScalaJSPluginInternal.scala:539)
  at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$45$$anonfun$apply$27$$anonfun$apply$28.apply(ScalaJSPluginInternal.scala:533)
  at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
[trace] Stack trace suppressed: run last toGhPages/compile:run for the full output.
java.lang.RuntimeException: Exception while running JS code: TypeError: Cannot call method "querySelector" of undefined (/Users/dwalend/.ivy2/cache/org.webjars/d3js/jars/d3js-3.5.5-1.jar#META-INF/resources/webjars/d3js/3.5.5/d3.min.js#3)
  at scala.sys.package$.error(package.scala:27)
  at org.scalajs.jsenv.rhino.RhinoJSEnv.org$scalajs$jsenv$rhino$RhinoJSEnv$$internalRunJS(RhinoJSEnv.scala:173)
  at org.scalajs.jsenv.rhino.RhinoJSEnv$Runner.run(RhinoJSEnv.scala:62)
  at org.scalajs.sbtplugin.ScalaJSPluginInternal$.org$scalajs$sbtplugin$ScalaJSPluginInternal$$jsRun(ScalaJSPluginInternal.scala:479)
  at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$45$$anonfun$apply$27$$anonfun$apply$28.apply(ScalaJSPluginInternal.scala:539)
  at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$45$$anonfun$apply$27$$anonfun$apply$28.apply(ScalaJSPluginInternal.scala:533)
  at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
此错误表明我的代码正在执行它应该执行的操作。然而,互联网智慧说

我怀疑我在系统中丢失了一些sbt开关,但不知道还能找到什么

我也不知道它应该如何工作。我不熟悉javascript,但我不知道这些javascript文件中的任何一个是如何依赖于任何生成的文件中的任何其他文件的。(上的示例使用index.html页面中的脚本标记将所有内容链接在一起。)


总体情况:我正在尝试使用sbt、scala.js和d3为scala图形算法库创建性能图表。第一组图表看起来很有希望,但github在README.md页面上不支持javascript。为此,我需要一个简单的图像。我想了解更多有关scala.js和d3的信息,它们吸引了我使用这种方法。

Quickfix

为了在Node.js中工作,请不要正确声明要显示的成员(即:no
var
或named
function
):

这是一个非常糟糕的黑客攻击,但可以解决
algorithmTime.js
的包含问题。最后是“适当”的解决方案

背景

在一般情况下编写不同的JavaScript文件是很困难的,因为没有标准化的方法。传统的HTML包含标记只是具有连接所有代码的语义。这就是我们试图在Scala.js运行程序中模拟的语义

但是,Node.js使用模块系统。在该系统中,库显式导出成员,使用站点将其放入命名空间中。这样可以避免命名冲突

例如:

// Library (foo.js)
exports.foo = function() { return 1; };

// Using code
var lib = require("foo.js");
lib.foo() // returns 1
这允许库声明本地值,而不会将它们泄漏到调用方。(请注意:尽管我们在这里有一个名为
require
的函数,但它不是

然而,在Scala.js Runner中,我们希望“只包含”
foo.js
,这是一个挑战。对于
require
调用的结果,我们应该使用什么名称?这就是
commonJSName
的意思(参见下面的示例)

如果未为给定依赖项设置
commonJSName
,则在Node.js运行程序中,我们将只发出

require(<name.js>);
现在我们做到了:

require("bar.js")
console.log(a); // undefined
console.log(b); // 2
似乎
b
会泄漏到全局上下文中,而
a
则不会。这就是quickfix工作的原因

解决方案

要获得更好的解决方案,您有两种选择:

  • 提交到Node.js,编写特定于其模块系统的库
  • 自动检测所包含的环境并动态调整(许多JS库都会这样做)
  • 解决方案1

    modules.exports = function() {
      console.log("hello from js")
    };
    
    commonJSName
    添加到依赖项:

    jsDependencies += ProvidedJS / "algorithmTime.js" commonJSName "hello"
    
    这将在Node.js以外的任何地方失败,原因有二:

  • JS虚拟机可能不支持CommonJS样式包含
  • 覆盖完整的
    导出类似的
    命名空间不是标准的CommonJS,而是特定于Node.js(IIRC)
  • 解决方案2

    自动检测:

    var hello = {};
    
    // Scope to prevent leakage
    (function(exp) {
      hello.hello = function() {
        console.log("hello from js");
      }
    })(exports ? exports : hello);
    
    在这种情况下,还需要设置
    commonJSName

    此外,您可能已经从代码中怀疑,这需要额外的间接寻址,因为CommonJS要求顶级导出为对象(IIRC)。因此,您需要调整Scala.js代码:

    global.hello.hello();
    
    但是,如果库导出多个符号,这可能是一个好主意。此外,这可能适用于大多数JS环境(并且应该适用于Scala.JS提供的三种环境)

    结语

    我们(Scala.js团队)对这种情况非常不满意,因为我们认为包含js库应该和依赖jvmland中的其他Scala和/或Java库一样简单。但是,我们还没有找到更好的解决方案来支持每一个包含样式,这是一项巨大的设计、工程和维护工作(如果系统发生变化或出现新系统怎么办?)


    相关讨论:和。

    您遇到了Scala.js导入其他JavaScript的基本限制之一(在ES5中没有标准化,因此我们只能依赖启发式)。今天我没有时间写一个完整的答案。明天(太平洋标准时间)我会写一份详细的答案。我期待着它。谢谢当我再次看到这个时,我意识到这可能不适用于Node.js——querySelector是一个DOM函数,我不希望看到它在Node中实现。我建议在PhantomJS上再次尝试,使用D3的非精简版本,看看是否可以在那里获得行号。我认为这才是真正的问题所在:D3正试图根据错误消息调用一个等于空指针的方法……感谢您的快速修复和深入解释。快速修复工作!不幸的是,我从js[error][stdin]:27[error]t}:function(n,t){for(var e in t)n[e]=t[e]},Ma=function(n,t){return t.querySele[error][error]TypeError:无法读取Ma([stdin]:27:9927)[error]at Object ta.select([stdin 27:15439][error]在plotIt(…/target/scala-2.11/classes/algorithmTime.js:27:18)[错误]在dataToPng(…/target/scala-2.11/classes/algorithmTime.js:99:5)
    modules.exports = function() {
      console.log("hello from js")
    };
    
    jsDependencies += ProvidedJS / "algorithmTime.js" commonJSName "hello"
    
    var hello = {};
    
    // Scope to prevent leakage
    (function(exp) {
      hello.hello = function() {
        console.log("hello from js");
      }
    })(exports ? exports : hello);
    
    global.hello.hello();