Open glmars opened 5 years ago
I'm running into this issue as well. I'm pretty sure when we initially implemented LibraryOnly the fastopt / fullopt outputs were wrapped in an IIFE. @julienrf do you happen to know if the requirement on CommonJSModule is new, or if the upstream behavior has changed?
It appears as if the IIFE on commonjs modules disappeared in scala.js 0.6.16 -- see https://github.com/scala-js/scala-js/issues/2785
As a workaround, you can use Application mode with fullOpt, you'll just lose the properly functioning sourcemaps. So far, I have not been able to get application mode with fullopt to work properly with webpack 4, ymmv.
Yes, as workaround we use BundlingMode.LibraryOnly
only for fastOptJS
:
webpackBundlingMode in fastOptJS := BundlingMode.LibraryOnly()
Do you think we need a new option for the scala-js
compiler to preserve IIFE to solve this issue?
I think the correct solution is to consume ES modules instead of CommonJS modules from scala.js, but I could be totally wrong on that. @sjrd would probably know if that's a reasonable direction to pursue.
As a follow up, I did get full application bundling working with Webpack 4 by eliminating webpack minification process. It might be possible to "tune" it instead of turning it off entirely but I haven't pursued that yet.
CommonJS modules do not need to be IIFE'ed. Their specification says that they are semantically scoped in their own scope. If a tool breaks that semantic, it is the tool's fault.
ES modules go even further: they must not be IIFE'ed, because export
and import
must be at the top-level of the module.
There's nothing Scala.js core can/should do here.
@sjrd is there some other path to getting an isolated module out of Scala.js? Re wrapping the entire file with the library ends up destroying the sourcemaps let alone the processing delay.
A CommonJS module is isolated. So is an ES module. It's Webpack or its configuration that is destroying the isolation. Scala.js can't do anything.
@sjrd webpack isn't involved here. In BundlingMode.LibraryOnly we extract the dependencies from the Scala.js output and use them to trick webpack into creating a bundle of all the dependencies, then load the Scala.js output file directly into the browser via a script tag.
The issue we're trying to work around here is that the Scala.js output file isn't getting loaded into the browser in an isolated context. Is there no Scala.js output mode that's designed for direct loading into a browser via a script tag?
After some further investigation, I think I have the path forward. @sjrd I'd appreciate your feedback on the approach.
Net-net ES2015 modules are here. Scala.js can output them, and if we can import them directly into modern browsers with <script type="module" ... >
. We'll have to adjust some stuff on the bundler side of things to make the webpack library available as an ES module, but assuming we can figure that part of things out we'll be properly encapsulated and not have to rely on nasty hacks like
<script language="JavaScript">
var exports = window;
exports.require = window["ScalaJSBundlerLibrary"].require;
</script>
Make sense?
After some further investigation, it doesn't look like webpack can emit an ES2015 module so we'd have to use rollup to create the library bundle. https://github.com/webpack/webpack/issues/2933
I think I ran into this, and it was because I was including the foo-fastOpt.js
file instead of the file processed by webpack. I recommend looking at https://github.com/shadaj/create-react-scala-app.g8/tree/master/src/main/g8 it basically helped me out by looking at @shadaj's configuration setup.
The issue we're trying to work around here is that the Scala.js output file isn't getting loaded into the browser in an isolated context. Is there no Scala.js output mode that's designed for direct loading into a browser via a script tag?
There is. It's the default ModuleKind.NoModule
. But that does not allow to use @JSImport
, only global variables. The library-mode seems to be a hack that wants to load a CommonJS module as a script, and as such it breaks the isolation spec'ed for CommonJS modules.
Using ES 2015 modules could solve your issues, as they are natively supported modules with the appropriate isolation.
Thanks @sjrd, that's what I suspected. The trick with going direct ES-to-the-browser is going to be automating making the dependency tree available to the browser as ES modules -- I'm not even sure how (or if) it's being done in anger by the ES community. My first attempt at bundling with rollup ran into snags due to invalid common-js code in my dependency tree so it's not looking trivial.
@dispalt that's an interesting workaround. It looks like they have replaced the webpack config for fastopt with one that has been modified to load the -fastopt file with a "noparse" flag. My assumption is that it speeds up the webpack process considerably, probably to the point of being almost no overhead when using dev server. In a play or sbt plugin workflow it will still invoke webpack though so it will never be quite as fast as directly including the -fastopt file.
All that being said, this problem is not nearly as bad with -fastopt. Most (all?) of the global symbols are long and have "$" or "scalajs" in the name so they don't conflict much. Things really blow up when you try to use the optimized file where they've all turned into one and two character variable names.
In the interim, it would probably be good to document that it's not recommended to use LibraryOnly
with FullOpt
.
His workflow is completely dependent on using webpackDevServer. I use play and still use webpackDevServer, still get auto compiling, its really complicated though because of how much play assumes (which is sort of it's point, in all fairness).
scalajs-bundler
enablesCommonJSModule
module kinde, soscala-js
doesn't wrap JS output to IIFE wrapper (please, see here) and in this case we have the same problem as described in https://github.com/scala-js/scala-js/issues/69 (especially onfullOptJS
output)