scalacenter / scalajs-bundler

https://scalacenter.github.io/scalajs-bundler
Other
234 stars 101 forks source link

top level anonymous given gives syntax error when loaded in browser #385

Open weihsiu opened 3 years ago

weihsiu commented 3 years ago

scala 3.0.0-M2

import cats._
import cats.syntax.all._

trait CanSing[F[_]]:
  def sing(song: String): F[String]

given [F[_] : Applicative] as CanSing[F]:
  def sing(song: String) = song.pure

object TestMain:

  @main
  def run(): Unit =
    val canSing = summon[CanSing[Option]]
    println(canSing.sing("bad") *> canSing.sing("good"))

above compiles and bundles fine but the browser raises "Uncaught SyntaxError: Invalid or unexpected token" when loading the bundled js. looking into the bundled js, the following js seems to be the culprit:

...TestMain$package$given_CanSing_F__f_evidence$1=null,...

if i give a name to the anonymous given so it's no longer anonymous, the bundled js loads fine.

another curious thing is this bundling is done under the setting "webpackEmitSourceMaps := false". if i turn it on, "fastOptJS::webpack" will give error:

[info] Bundling the application with its NPM dependencies
[error] .../consumer/target/scala-3.0.0-M2/scalajs-bundler/main/node_modules/source-map/lib/source-map-generator.js:298
[error]       throw new Error('Invalid mapping: ' + JSON.stringify({
[error]       ^
[error] Error: Invalid mapping: {"generated":{"line":1173,"column":2},"source":".../consumer/src/main/scala/trueown/client/consumer/TestMain.scala","original":{"line":8,"column":-1},"name":null}
[error]     at SourceMapGenerator_validateMapping [as _validateMapping] (.../consumer/target/scala-3.0.0-M2/scalajs-bundler/main/node_modules/source-map/lib/source-map-generator.js:298:13)
[error]     at SourceMapGenerator_addMapping [as addMapping] (...
sjrd commented 3 years ago

Thanks for the report. Can this be reproduced without scalajs-bundler?

weihsiu commented 3 years ago

No, plain fastOptJS works fine.

On Mon, Nov 30, 2020, 2:41 AM Sébastien Doeraene notifications@github.com wrote:

Thanks for the report. Can this be reproduced without scalajs-bundler?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/scalacenter/scalajs-bundler/issues/385#issuecomment-735436926, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAHSG2OCFVPMSMXBFG7MA3SSKIVTANCNFSM4UGVTV5A .

sjrd commented 3 years ago

So, here is what happens:

  1. Scala 3 generates names for anonymous givens, and uses the character FULL WIDTH LOW LINE (_, \uff3f) as connector between the parts. So for example the anonymous given here is called given_CanSing_F.
  2. Since _ is a valid character for a JavaScript identifier, Scala.js keeps it as is. However, the Scala.js linker always emits an ASCII output, so it escapes it as \uff3f in the .js file.
  3. Webpack correctly parses that as being _, but when it emits its output it does not escape it. Instead it writes it down as is, encoded as UTF-8 (which is perfectly valid per se as well; it's just "less safe" than what Scala.js core does).
  4. Your browser/editor seems to interpret that .js as being encoded in latin1 (ISO-8859-1). Under that encoding, the UTF-8 bytes of _ are interpreted as _, which contains characters that are not valid in a JS identifier.
  5. The source map generator may also have an issue with that UTF-8 sequence, although it's unclear how that translates into the error we see.

My conclusions:

weihsiu commented 3 years ago

you are absolutely right. adding attribute 'charset="utf-8"' to the script tag solves the problem. so somehow my chrome defaults to latin1 when interpreting js. when i tested in firefox, it works with or without 'charset="utf-8"'.

adding <meta charset="UTF-8"> in the head does the trick as well. but all these still doesn't explain the error i encountered when generating source map though.

sjrd commented 3 years ago

I believe it's the same kind of issue for the source map stuff. It's that whatever code trying to parse/load the .js file to then construct source maps, somewhere inside Webpack and its dependencies, defaults to reading in latin1. It might be possible to produce a small reproduction for that that is independent of Scala.js or scalajs-bundler.

adpi2 commented 3 years ago

I have a similar issue with a project as simple as:

// project/build.properties
sbt.version=1.4.6
// project/plugins.sbt
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.3.1")
addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.20.0")
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.5.1")
// build.sbt
lazy val root = (project in file(".")).
  settings(
    scalaVersion := "3.0.0-M3",
    scalaJSUseMainModuleInitializer := true,
    // webpackEmitSourceMaps := false
  )
  .enablePlugins(ScalaJSPlugin, ScalaJSBundlerPlugin)
// src/main/scala/Main.scala
object Main extends App {
  println("Hello, World!")
}
> sbt fastOptJS / webpack
[info] compiling 1 Scala source to /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/classes ...
[info] Fast optimizing /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/root-fastopt
[info] Updating NPM dependencies
[error] npm WARN deprecated chokidar@2.1.8: Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.
[error] npm WARN deprecated fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.
[error] npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
[error] npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
[info] > webpack-cli@3.3.2 postinstall /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack-cli
[info] > node ./bin/opencollective.jsendencies 11s
[info]                             Thanks for using Webpack!
[info]                  Please consider donating to our Open Collective
[info]                         to help us maintain this package.
[info]                  Donate: https://opencollective.com/webpack/donate
[error] npm notice created a lockfile as package-lock.json. You should commit this file.
[error] npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.3.1 (node_modules/chokidar/node_modules/fsevents):
[error] npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.1: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
[error] npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.2.7 (node_modules/watchpack-chokidar2/node_modules/chokidar/node_modules/fsevents):
[error] npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
[error] npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.2.7 (node_modules/webpack-dev-server/node_modules/chokidar/node_modules/fsevents):
[error] npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
[info] added 590 packages from 364 contributors and audited 593 packages in 12.508s
[info] 26 packages are looking for funding
[info]   run `npm fund` for details
[info] found 2 low severity vulnerabilities
[info]   run `npm audit fix` to fix them, or `npm audit` for details
[info] Writing scalajs.webpack.config.js
[info] Bundling the application with its NPM dependencies
[error] /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/source-map/lib/source-map-generator.js:298
[error]       throw new Error('Invalid mapping: ' + JSON.stringify({
[error]       ^
[error] Error: Invalid mapping: {"generated":{"line":699,"column":0},"source":"/home/piquerez/adpi2/foo/src/main/scala/Main.scala","original":{"line":0,"column":6},"name":null}
[error]     at SourceMapGenerator_validateMapping [as _validateMapping] (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/source-map/lib/source-map-generator.js:298:13)
[error]     at SourceMapGenerator_addMapping [as addMapping] (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/source-map/lib/source-map-generator.js:110:12)
[error]     at /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/source-map/lib/source-node.js:351:13
[error]     at SourceNode_walk [as walk] (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/source-map/lib/source-node.js:230:9)
[error]     at SourceNode_walk [as walk] (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/source-map/lib/source-node.js:226:13)
[error]     at SourceNode_walk [as walk] (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/source-map/lib/source-node.js:226:13)
[error]     at SourceNode_toStringWithSourceMap [as toStringWithSourceMap] (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/source-map/lib/source-node.js:342:8)
[error]     at ConcatSource.proto.sourceAndMap (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack-sources/lib/SourceAndMapMixin.js:29:32)
[error]     at CachedSource.sourceAndMap (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack-sources/lib/CachedSource.js:58:31)
[error]     at getTaskForFile (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack/lib/SourceMapDevToolPlugin.js:37:30)
[error]     at /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack/lib/SourceMapDevToolPlugin.js:136:20
[error]     at Array.forEach (<anonymous>)
[error]     at /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack/lib/SourceMapDevToolPlugin.js:130:12
[error]     at SyncHook.eval [as call] (eval at create (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/tapable/lib/HookCodeFactory.js:19:10), <anonymous>:7:1)
[error]     at SyncHook.lazyCompileHook (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/tapable/lib/Hook.js:154:20)
[error]     at /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack/lib/Compilation.js:1319:42
[error]     at AsyncSeriesHook.eval [as callAsync] (eval at create (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:6:1)
[error]     at AsyncSeriesHook.lazyCompileHook (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/tapable/lib/Hook.js:154:20)
[error]     at /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack/lib/Compilation.js:1315:36
[error]     at AsyncSeriesHook.eval [as callAsync] (eval at create (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:6:1)
[error]     at AsyncSeriesHook.lazyCompileHook (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/tapable/lib/Hook.js:154:20)
[error]     at /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack/lib/Compilation.js:1311:32
[error]     at AsyncSeriesHook.eval [as callAsync] (eval at create (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:6:1)
[error]     at AsyncSeriesHook.lazyCompileHook (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/tapable/lib/Hook.js:154:20)
[error]     at Compilation.seal (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack/lib/Compilation.js:1248:27)
[error]     at /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack/lib/Compiler.js:625:18
[error]     at /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack/lib/Compilation.js:1171:4
[error]     at AsyncSeriesHook.eval [as callAsync] (eval at create (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
[error]     at AsyncSeriesHook.lazyCompileHook (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/tapable/lib/Hook.js:154:20)
[error]     at Compilation.finish (/home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack/lib/Compilation.js:1163:28)
[error] Failure on parsing the output of webpack: No content to map due to end-of-input
[error]  at [Source: (ProcessPipeInputStream); line: 1, column: 0]
[error] You can try to manually execute the command
[error] node /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/node_modules/webpack/bin/webpack --bail --profile --json --config /home/piquerez/adpi2/foo/target/scala-3.0.0-M3/scalajs-bundler/main/scalajs.webpack.config.js
[error] 
[error] stack trace is suppressed; run last Compile / fastOptJS for the full output
[error] (Compile / fastOptJS / webpack) Non-zero exit code: 1
[info] fastOptJS / webpack completed
[error] Total time: 14 s, completed Jan 7, 2021 5:51:58 PM

I am not familiar enough with webpack and source-map to find the cause of this error in the produced .js or .map files. With webpackEmitSourceMaps := false it works well and I can execute the bundled file in the browser.

mims-github commented 2 years ago

Similar issue when running sbt fastOptJS / webpack with webpack source-map, after changing Scala from 2.13.8 to 3.1.2.

[info] Bundling the application with its NPM dependencies
[error] .../target/scala-3.1.1/scalajs-bundler/main/node_modules/source-map/lib/source-map-generator.js:298
[error]       throw new Error('Invalid mapping: ' + JSON.stringify({
[error]       ^
[error] Error: Invalid mapping: {"generated":{"line":1144,"column":134},"source":"...REMOVED...scala","original":{"line":2,"column":-1},"name":null}
// project/build.properties
sbt.version=1.6.2
// project/plugins.sbt
addSbtPlugin("org.scala-js"  % "sbt-scalajs"         % "1.10.0")
addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.20.0")
// build.sbt
enablePlugins(ScalaJSPlugin)
enablePlugins(ScalaJSBundlerPlugin)

scalaVersion := "3.1.2"

scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.CommonJSModule) }
// webpackEmitSourceMaps := false
mims-github commented 2 years ago

I can also confirm, that my js file output from fastOptJS also contains the \uff3f char. e.g.

  var _\uff3fself = this$1.EMPTY_SET__ju_Set();
mims-github commented 2 years ago

Another side note: switching from fastOptJS to fullOptJS also "fixes" it for us. So sbt fullOptJS/webpack works even without webpackEmitSourceMaps := false.