Closed johnhungerford closed 5 months ago
Aha, interesting. IIUC, there is more than one problem described here:
It would be great if you could use scalajs-vite to bundle and run tests. The basic approach is pretty straightforward: use the Test / fastLinkJS outputs as the bundler input and pass the output to Test / jsEnvInput.
I was playing around with this implementation here - https://github.com/ptrdom/scalajs-vite/tree/test-bundling. I think it covers the basic use cases, but I might be missing something. Would it be a good solution?
One edge case here that is easily missed is that while tests might need to have access to both node_modules and other non-Scala.js sources, those non-Scala.js source might in turn need access to Compile / xLinkJs outputs. This means that the build context where tests are bundled should also include Compile Scala.js outputs.
I assume that these exports need to be bundled, otherwise this would be outside of this project's scope. I think you are describing library bundling, this should be achievable now with custom Vite
config. Would that work for you?
I was playing around with this implementation here - https://github.com/ptrdom/scalajs-vite/tree/test-bundling. I think it covers the basic use cases, but I might be missing something. Would it be a good solution?
I think that's what I have in mind, although I'm still not 100% on how the scoping works. Do you have separate target directories for Test
and Compile
?
I assume that these exports need to be bundled, otherwise this would be outside of this project's scope. I think you are describing library bundling, this should be achievable now with custom Vite config. Would that work for you?
Yes I'm thinking in the case of Scala.js exports being bundled, but this need not mean using vite in library mode (although it would be good to support that use case). Here are three cases in which a runnable (non-library) Scala.js project would need to support resolving imports from Scala.js:
App.jsx
file that imports 'scalajs:ScalaJSComponent1.js'
and 'scalajs:ScalaJSComponent2.js'
. I expect vite to bundle App.jsx
with Scala.js artifacts produced by fullLinkJS
(which I expect to include ScalaJSComponent1.js
and ScalaJSComponent2.js
)fullLinkJS
produces main.js
(runnable entrypoint), and utils.js
(module generated by a call to JSExportTopLevel("someUtility", "utils")
). main.js
imports a JS module someJsModule.js
, and someJsModule.js
in turns imports 'scalajs:utils.js'
. These imports all need to be resolvable for vite to generate a runnable bundle from main.js
.someJsModule.js
. For this test to run, the bundler is going to need access not only to the compiled test code and the JS sources, but also the compiled Scala.js application code, since theTest / fastLinkJS
outputs will not necessarily include the utils.js
export!Your application entrypoint is JavaScript. Example: I have a JS react app, and one or more components of it are in Scala.js. I have some App.jsx file that imports 'scalajs:ScalaJSComponent1.js' and 'scalajs:ScalaJSComponent2.js'. I expect vite to bundle App.jsx with Scala.js artifacts produced by fullLinkJS (which I expect to include ScalaJSComponent1.js and ScalaJSComponent2.js)
So I think here you should have your Scala.js components exported as a library and that is that. scalajs-vite
would only do the library bundling, if said Scala.js components depend on npm
dependencies.
Your application entrypoint is Scala.js, but it depends on some non-Scala.js sources. These sources in turn depend on exports from Scala.js. Imagine, e.g., fullLinkJS produces main.js (runnable entrypoint), and utils.js (module generated by a call to JSExportTopLevel("someUtility", "utils")). main.js imports a JS module someJsModule.js, and someJsModule.js in turns imports 'scalajs:utils.js'. These imports all need to be resolvable for vite to generate a runnable bundle from main.js.
As a corollary to (2), now image you have a test in your Scala.js source that includes the dependency to someJsModule.js. For this test to run, the bundler is going to need access not only to the compiled test code and the JS sources, but also the compiled Scala.js application code, since theTest / fastLinkJS outputs will not necessarily include the utils.js export!
This is a highly custom workspace-like workflow that I think should be split up and not implemented directly by this project. In npm
world you would likely have utils.js
package, someJsModule.js
package and main.js
application that uses those packages. This is only missing library bundling and we can add support for that. Now the gluing of the workflow can be done outside of this project. Do you see this working for you?
In npm world you would likely have utils.js package, someJsModule.js package and main.js application that uses those packages.
utils.js
isn't a package, it's just a script that's emitted by the Scala.js linker; similarly, someModule.js
is just a javascript source file. The question is only whether the bundler can resolve import
statements that reference them. I promise this has nothing to do with library bundling. When you run fullLinkJS
or fastLinkJS
, the linker will produce one or more files (depending on your linker configuration and what kind of JSExport
annotations you include in your code). There is no reason why other non-Scala.js source files included in viteResourcesDirectory
should not be able to import from them.
At any rate, I definitely understand if you don't want to support complex interop here, as it will definitely increase the complexity, but it's a requirement for me. I already have a working solution for it, so if you don't want to move in that direction, I can just keep working on my project. I just thought it would make sense to deduplicate our efforts.
But then if utils.js
is not a standalone library, I do not see what is the problem. For example, how case 1 and 2, and even 3 with https://github.com/ptrdom/scalajs-vite/tree/test-bundling merged, cannot be made to work with current version of this project right now? It might need more than one Scala.js module to be emitted, but it should not need multiple sbt modules.
Main thing I am suggesting is to try and decompose your problem into smaller bits that would make smaller, reusable features, then determine what is within scope of this project. If you are not willing to proceed in this manner, then working on your own plugin will make most sense.
And you will need to have some patience with me, because I am not familiar with this particular workflow 🤷
Actually I just went back and tried something out and it looks like I had an incorrect assumption: the exported modules are included in the Test / xLinkJS
outputs! I must have missed something when I tested this before. (It had looked to my like Test / xLinkJS
didn't generate exports, in which case Compile / xLinkJS
outputs would need to be included in the build directory when bundling tests to support case 3)
This being the case, you are correct that your approach should be able to handle all three cases.
Implemented with https://github.com/ptrdom/scalajs-vite/pull/89.
It would be great if you could use scalajs-vite to bundle and run tests. The basic approach is pretty straightforward: use the
Test / fastLinkJS
outputs as the bundler input and pass the output toTest / jsEnvInput
.One edge case here that is easily missed is that while tests might need to have access to both node_modules and other non-Scala.js sources, those non-Scala.js source might in turn need access to
Compile / xLinkJs
outputs. This means that the build context where tests are bundled should also includeCompile
Scala.js outputs.For context: I am migrating a project from TS to Scala.js, and I'm reimplementing components piecemeal. I export those Scala.js components using
JSExportTopLevel("SomeComponent", "someModule")
, and I update the TS components that depend on them to import as:import { someComponent } from 'scalajs:someModule.js';
. If I want to run Scala.js tests that include those TS components, I need to also have the Scala.js outputs available.