-
Notifications
You must be signed in to change notification settings - Fork 101
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
Failure to parse Webpack stats fails the build #424
base: main
Are you sure you want to change the base?
Conversation
Related to #423. |
8170f89
to
bee2882
Compare
and related to #338 which was closed because I presumed the PR would get merged, but it never did - shame on me :) |
@@ -115,13 +115,13 @@ object Stats { | |||
)(Asset.apply _) | |||
|
|||
implicit val errorReads: Reads[WebpackError] = ( |
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.
@ptrdom what do you think of just using semi-auto derivation in Stats.scala
?:
implicit val assetsReads: Reads[Asset] = Json.reads[Asset]
implicit val errorReads: Reads[WebpackError] = Json.reads[WebpackError]
implicit val warningReads: Reads[WebpackWarning] = Json.reads[WebpackWarning]
implicit val pathReads: Reads[Path] = Reads.StringReads.map(new File(_).toPath)
implicit val statsReads: Reads[WebpackStats] = Json.using[Json.WithDefaultValues].reads[WebpackStats]
I saw this used in #408 and tested this works and saves a few lines of code ;)
@@ -50,9 +50,9 @@ object Stats { | |||
|
|||
} | |||
|
|||
final case class WebpackError(moduleName: String, message: String, loc: String) | |||
final case class WebpackError(moduleName: Option[String], message: String, loc: String) |
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.
the docs are a little unclear, but besides message
it sounds like "various" other properties means perhaps loc
should be optional too?:
https://webpack.js.org/api/stats/#errors-and-warnings
Also, @ptrdom did you try Curious to know how you managed to publish locally without that fix... |
@evbo I ran into the same issue and simply published the module explicitly, same as the CI workflow does it - https://github.com/scalacenter/scalajs-bundler/blob/main/.github/workflows/release.yml#L21-L22. Adding the project to aggregate makes sense to me. |
@sjrd Can we merge this? |
build.sbt
Outdated
.aggregate(`sbt-scalajs-bundler`, `sbt-web-scalajs-bundler`) | ||
.aggregate(`sbt-scalajs-bundler`, `sbt-web-scalajs-bundler`, `scalajs-bundler-linker`) |
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.
This shouldn't be in this PR.
Moreover, this is wrong anyway, because scalajs-bundler-linker
doesn't have the same cross-version setup as the other projects.
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.
that was my bad - whoops. See comment: #424 (comment)
Note: I am currently using scalajs-bundler published locally with that aggregate and it has been running fine, though I'm not sure of the implications to a different "cross-version setup".
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.
While I do understand that this change does not have to be with this PR, but there is still an issue related to publishing in development setup, rather than cross-version setup.
Current documentation for contributing notes that sbt publishLocal
is enough for local publishing, but that is false, since scalajs-bundler-linker
will not be published and it will result in errors. So either documentation needs to be updated or scalajs-bundler-linker
project has to be added to the aggregate.
Publishing workflow works around that by publishing each module individually -
https://github.com/scalacenter/scalajs-bundler/blob/main/.github/workflows/release.yml#L21-L22
@sjrd Am I correct that there is an issue and a solution is required?
implicit val errorReads: Reads[WebpackError] = Json.reads[WebpackError] | ||
implicit val warningReads: Reads[WebpackWarning] = Json.reads[WebpackWarning] | ||
implicit val pathReads: Reads[Path] = Reads.StringReads.map(new File(_).toPath) | ||
implicit val statsReads: Reads[WebpackStats] = Json.using[Json.WithDefaultValues].reads[WebpackStats] |
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.
These changes don't seem to have anything to do with the purpose of this PR, have they?
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.
@sjrd It does because you need to update the JSON decoders to parse the new Option
types that have been added. So rather than explicitly modify the individual fields it parses as Options, this solves the problem using derived decoders, which is also fewer lines and I don't think there are any downsides since it's compile-time derivation (maybe slightly longer time taken compiling?)
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.
Since the model has changed, Reads
do have to be modified, and derived decoders are a simplification of those changes.
if (notExisting.nonEmpty) { | ||
throw new RuntimeException( | ||
s"Webpack failed to create application assets:\n" + | ||
s"${notExisting.map(asset => s"${asset.getAbsolutePath}").mkString("\n")}") |
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.
I you're going to throw anyway, this method can be simplified a lot:
val assets = stats.resolveAllAssets(targetDir)
val nonExisting = assets.filter(!_.exists())
if (nonExisting.nonEmpty) {
throw ...
}
assets
.fold( | ||
sys.error, |
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.
If you're going to throw when the Try
is a Failure
, you as well do
val runResult = Commands.run(...).get
But then again, why change Commands.run
to return a Try
in that case, instead of directly throwing the exception?
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.
I tried to modify the existing code as little as possible, I see that expression was lost in a way with nesting of all these monads.
Command.run
returns an Either[String, Try[T]]]
where:
Left
means that command returned with a non-zero exit code, so wesys.error
it.Right(None)
means that command returned with a zero exit code, but had no output, so we throw athrow new RuntimeException("Failure on returning webpack stats from command output")
Right(Some(Failure))
means that command returned with zero exit code and had output, but output parsing toWebpackStats
withjsonOutput
has failed.Right(Some(Success)
means that command returned with zero exit code, had output and output parsing toWebpackStats
withjsonOutput
has succeeded.
So current implementation looks more funky because I wanted to separate the cases 2) and 3) - failures are related to command output, but mean different things.
Maybe this would have looked clearer?
Commands.run(cmd, workingDir, log, jsonOutput(cmd, log))
.fold(
sys.error,
_.fold(throw new RuntimeException("Command succeeded, but returned no webpack stats output"))(_.get)
)
Otherwise Command.run
would benefit from an improvement to return type specificity.
Knowing this, what further modifications would you suggest?
import sbt.Logger | ||
import java.io.{InputStream, File} | ||
import java.io.{File, InputStream} | ||
|
||
import scala.sys.process.Process |
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.
Spurious changes.
@@ -0,0 +1 @@ | |||
> fullOptJS::webpack |
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.
I don't understand how this test relates to the fix. If this PR is about failing a build, there should a be at least one negative test line (starting with -
).
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.
This test would fail in current main
branch, the changes added introduce fixes that actually make it pass. Is that fine?
d167f25
to
d33cb21
Compare
@sjrd I made changes for which I did not have further questions, please review the discussions and the code when you can. |
@sjrd Are the latest changes acceptable? Do you need any help with this? |
Wondering if this PR will be up for consideration in the future? I know many are moving to Vite, but our current build process is causing problems with Vite since we're packaging node modules for browser (Autoprefixer and postcss). Didn't have much luck working through (tried following advice postcss/postcss#830, but wasn't able to get it to work). Was hoping to upgrade to webpack5 on scalajs-bundler until we can refactor how we do CSS to not need autoprefixer running in the browser and then switch to Vite. |
Failure to parse Webpack stats can fail builds with certain Webpack configs. It should probably be a critical failure, rather than logging with return of an
Option
.