-
-
Notifications
You must be signed in to change notification settings - Fork 347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Give mill the ability to re-write ESModule imports at link time #3109
Conversation
I don't understand why this hasn't gone through :-(... time to down tools for now.... trying too reproduce locally. |
As far as I can tell, the failing task simply takes longer than 90 minutes - but then how can this work normally in CI? |
scalajslib/worker/1/src/mill/scalajslib/worker/ScalaJSWorkerImpl.scala
Outdated
Show resolved
Hide resolved
@lolgab Thankyou for being willing to look 🙏 ... I'm very grateful! The map actually should always be there - the new property of the |
I don't understand how that change, could have made it pass CI? I'm happy it's green though ... |
I'm seeing the Sbt implementation and it uses a function instead of a Map, which gives better flexibility. scalaJSImportMap := { (rawImport: String) =>
if (rawImport.startsWith("@shoelace-style/shoelace"))
"https://cdn.jsdelivr.net/npm/" + rawImport
else
rawImport
} which is possible with Sbt. I see you use a replace function created from the map, but it's a bit too powerful, because it can do things like. Map("node" -> "deno") and then you have Maybe a good tradeoff would be a Map of regexes, or globs? I read the conversation here and @armanbilge was referring to a possible implementation as an ADT, which is already being considered for Scala.js: VirtusLab/scala-cli#1968 (reply in thread) |
scalajslib/worker/1/src/mill/scalajslib/worker/ScalaJSWorkerImpl.scala
Outdated
Show resolved
Hide resolved
@lolgab With reference to the implementation as a map, I agree with your commentary, although I would be willing to defend the current implementation for a second look. Arman and I did discuss this further - I'm struggling to dig up the conversation, but the "map" structure, is somewhat in line with the the reference to how this would be done in pure javascript land. I believe it to be a partial implementation of the "official" way this capability works, which is indeed as a map. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules As you point out, it is serialisable, which is nice. I agree with your node -> deno example. Although this is bad, it's limited by being applied only to the "header" (I believe) emitted by the IR file, and I would not expect a reasonable person to attempt remapping in such a way. Normal entries are more like `"@shoelaceStyle/dist/" -> "https://cdn.place.com/shoelace/dist/", which should be rather unambiguous in the header of an ES Module. There is one further advantage of a pure map (I believe) being that it could encourage standardisation. The SBT implementation is much freer - but that implies everyone will end up writing their own functions, with all of the heterogeneity that implies. The pure map representation implies that, if library author (in either JS or scalaJS) has a well defined import, then everyone in the ecosystem will use the same entry in their map. I argue there is value in that. In this case, I think the freedom made sense during the exploration phase, and remains of value at the lowest level of the stack in case we've gotten this wrong and it needs revisiting... but I would defend the the status quo :-). Very open to the discussion... |
@Quafadas For sure the DX in simple cases is best with If we, instead, use a ADT we could support both cases at the same time. Example: def scalaJSImportMapping = T {
Seq(
ImportMapping.Regex("@shoelaceStyle\\/dist\\/(.+)", "https://cdn.place.com/shoelace/dist/$1"),
ImportMapping.Prefix("@shoelaceStyle/dist/", "https://cdn.place.com/shoelace/dist/")
)
} |
@lolgab I am agreed, but fearful of adding extra complexity. Jumping straight to regexing things would not be my personally preferred solution - I would bow to democratic will though. EDIT : There was a long, wrong comment here. |
I would start by opening a separate PR just for the cleanup and removing warnings, |
## Context I encountered an issue with the way scoverage plugin alters the dependency tree of the test module by changing its module dependency from the `outer` module to the `outer.scoverage` module. This shadow modification of the dependency tree produces a side effect that impacts the IDEA configuration generation. When the IDEA configuration generation resolves the dependencies instead of using the outer module as a dependency for the test project it depends on the `outer.scoverage` module which should not be generated as it does not exist factually but more virtually. ## Solution Instead of modifying the dependency tree at compile-time I change it at runtime by removing the `outer.localRunClasspath()` from the `test.runClasspath()` and appending, instead, the `outer.scoverage.localRunClasspath()` This resolves the issue of modifying the dependency tree but introduces one more compilation as when the `outer.localRunClasspath()` is resolved it will force the compilation of the outer module. In the previous version only the `outer.scoverage` was compiled and not both of them. Pull request: com-lihaoyi#3118
@lolgab Would it be possible to re-run the failing test? I think it should pass... I'm not 100% clear why sometimes that suite times out . |
Thankyou! |
@lolgab thanks for you tips so far. I think this would be ready for review, up to agreement on whether or not the simple string replace is a good mechanism. |
Review suggestions for `scalaJSImportMap`
@@ -270,3 +270,11 @@ object OutputPatterns { | |||
|
|||
implicit val rw: RW[OutputPatterns] = macroRW[OutputPatterns] | |||
} | |||
|
|||
sealed trait ESModuleImportMapping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -266,6 +275,10 @@ trait ScalaJSModule extends scalalib.ScalaModule { outer => | |||
|
|||
def scalaJSOptimizer: Target[Boolean] = T { true } | |||
|
|||
def scalaJSImportMap: Target[Seq[ESModuleImportMapping]] = T { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's wrong with the current name? What's the name of the underlying concept?
Other than the names I asked, it looks good to me 👍 |
It looks good to me. It is built in a way that can be easily extended in a binary compatible way. |
@lolgab thank you so much for your willingness to support this and your help 🙏 . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
Motivation
I'm really enjoying frontend scala, without needing to configure an entire node / npm environment. The capability to use the JS ecosystem without a bundler, is the "build primitive", that enables this. Here's a longer discussion of the motivation
VirtusLab/scala-cli#1968 (comment)
Implementation
arman added this to SBT here and published a library that does the heavy lifting.
https://github.com/armanbilge/scalajs-importmap
I followed this up in scala-cli ...
VirtusLab/scala-js-cli#47
... and am really enjoying this in the small. My larger projects are in mill though (thanks :-)!). Hence... this PR... which seeks to integrate the capability into mill. I wanted to do it in a plugin - but I couldn't see how as the call to the linker is in a private scope - so I've put it up for mill itself.
I would expect this as is to pass CI with the new test. Very open to feedback. If accepted, it would be my first contribution to mill ... I'd be a little surprised if I got everything right straight out the gate - a plugin may indeed be preferable if I have not correctly understood the constraints.