Skip to content
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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

ptrdom
Copy link
Contributor

@ptrdom ptrdom commented May 13, 2022

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.

@ptrdom
Copy link
Contributor Author

ptrdom commented May 13, 2022

Related to #423.

@ptrdom ptrdom marked this pull request as draft May 13, 2022 18:37
@ptrdom ptrdom marked this pull request as ready for review May 14, 2022 07:43
@ptrdom ptrdom force-pushed the webpack-stats-parse-failure branch from 8170f89 to bee2882 Compare May 14, 2022 17:30
@evbo
Copy link

evbo commented May 14, 2022

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] = (
Copy link

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)
Copy link

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

@evbo
Copy link

evbo commented May 14, 2022

Also, @ptrdom did you try sbt publishLocal with your fork? I tried it and my project failed to resolve it as a dependency due to scalajs-bundler-linker not getting published locally. I had to add it to the build.sbt aggregate statement here:
https://github.com/scalacenter/scalajs-bundler/blob/main/build.sbt#L90

Curious to know how you managed to publish locally without that fix...

@ptrdom
Copy link
Contributor Author

ptrdom commented May 15, 2022

@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.

@ptrdom
Copy link
Contributor Author

ptrdom commented May 15, 2022

@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`)
Copy link
Collaborator

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.

Copy link

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".

Copy link
Contributor Author

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]
Copy link
Collaborator

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?

Copy link

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?)

Copy link
Contributor Author

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")}")
Copy link
Collaborator

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

Comment on lines +282 to +283
.fold(
sys.error,
Copy link
Collaborator

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?

Copy link
Contributor Author

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:

  1. Left means that command returned with a non-zero exit code, so we sys.error it.
  2. Right(None) means that command returned with a zero exit code, but had no output, so we throw a throw new RuntimeException("Failure on returning webpack stats from command output")
  3. Right(Some(Failure)) means that command returned with zero exit code and had output, but output parsing to WebpackStats with jsonOutput has failed.
  4. Right(Some(Success) means that command returned with zero exit code, had output and output parsing to WebpackStats with jsonOutput 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?

Comment on lines 3 to 6
import sbt.Logger
import java.io.{InputStream, File}
import java.io.{File, InputStream}

import scala.sys.process.Process
Copy link
Collaborator

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
Copy link
Collaborator

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 -).

Copy link
Contributor Author

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?

@ptrdom ptrdom force-pushed the webpack-stats-parse-failure branch from d167f25 to d33cb21 Compare May 20, 2022 17:44
@ptrdom
Copy link
Contributor Author

ptrdom commented May 20, 2022

@sjrd I made changes for which I did not have further questions, please review the discussions and the code when you can.

@annedroiid
Copy link

@sjrd Are the latest changes acceptable? Do you need any help with this?

@rmmeans
Copy link

rmmeans commented Feb 13, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants