Skip to content

Commit 7cb64b5

Browse files
authored
Report the errors Fable raises, and fail the build on them (#67)
1 parent ab3c754 commit 7cb64b5

22 files changed

Lines changed: 327 additions & 55 deletions

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) from version [0.1.0] moving forward.
77

8+
## [0.4.0] - 2026-09-03
9+
10+
### Fixed
11+
12+
- An F# file that type-checks but that Fable cannot translate now fails `vite build` instead of passing for a successful compile. `Fable.Compiler` filled a compile's diagnostics from the F# type-check only and discarded Fable's own logs, so a call like `Async.RunSynchronously` produced a module that does nothing while the build printed nothing and exited 0, and the app broke in the browser. Fixed upstream in [fable-compiler/Fable#4923](https://github.com/fable-compiler/Fable/pull/4923), which this release picks up: the daemon now reports what Fable said alongside the F# diagnostics, for the first compile of the project as well as for every recompile after an edit, so these errors reach the terminal, the browser overlay and `/api/diagnostics`. They carry no error number, so they are named by their tag — `ERROR FABLE:` where an F# diagnostic reads `ERROR FS0025:`. Diagnostics on files under `fable_modules` are still dropped unless `fableModulesDiagnostics` is on. ([#67](https://github.com/fable-compiler/vite-plugin-fable/pull/67))
13+
14+
### Changed
15+
16+
- Updated Fable.Compiler to 5.15.0 and `@fable-org/fable-library-js` to 2.6.0. ([#67](https://github.com/fable-compiler/vite-plugin-fable/pull/67))
17+
818
## [0.3.1] - 2026-08-29
919

1020
### Changed

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ else's contract:
2121
hook-contract questions definitively. Several plugin bugs were misdiagnosed from assumptions
2222
about Vite that the source contradicted, and several fixes in the git history cite line numbers
2323
from it.
24-
- `../Fable` — Fable 5.14. Notably `src/Fable.Compiler/Library.fs`, where
24+
- `../Fable` — Fable 5.15. Notably `src/Fable.Compiler/Library.fs`, where
2525
`FileWriter.AddSourceMapping` is a no-op, so `CliArgs.SourceMaps` does nothing.
2626

2727
## Seeing what the plugin actually emitted, without a browser

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<PackageVersion Include="FSharp.Core" Version="10.1.203" />
99
<PackageVersion Include="FSharp.SystemTextJson" Version="1.4.36" />
1010
<PackageVersion Include="StreamJsonRpc" Version="2.25.29" />
11-
<PackageVersion Include="Fable.Compiler" Version="5.14.1" />
11+
<PackageVersion Include="Fable.Compiler" Version="5.15.0" />
1212
<PackageVersion Include="Suave" Version="2.6.2" />
1313
<PackageVersion Include="protobuf-net" Version="3.2.30" />
1414
<!-- lock transitive dependencies -->

ROADMAP.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@ Nothing here is done. Items are deleted as they land, so what remains is open wo
44

55
Item 1 is blocked upstream and can only be tracked and item 2 is on hold. Item 3 is a project, items 4 and 5 are smaller ones, item 6 records a rejected decision and item 7 is loose ends.
66

7-
References into `~/Projects/Fable` are against Fable 5.14, the version in the workspace catalog.
7+
References into `~/Projects/Fable` are against Fable 5.15, the version in the workspace catalog.
88

99
## 1. Blocked on Fable
1010

11-
Neither of these can be fixed in this repo. Both need an upstream change first.
11+
This cannot be fixed in this repo. It needs an upstream change first.
1212

13-
- **Fable's own errors never reach the plugin.** `CodeServices.compileMultipleFilesToJavaScript` fills `CompileResult.Diagnostics` from FCS's type-check results only, and discards the `CompilerImpl` holding `com.Logs` (`~/Projects/Fable/src/Fable.Compiler/Library.fs:223` upcasts it to the `Compiler` interface, where `Logs` does not exist). A file that type-checks but that Fable cannot translate therefore compiles to `return null` with no diagnostic at all: `vite build` prints nothing, exits 0, and the app breaks at runtime. Reproduced in `sample-project` with `Async.RunSynchronously`, and filed with a proposed direction as [fable-compiler/Fable#4922](https://github.com/fable-compiler/Fable/issues/4922).
14-
There is a local half waiting on it: `FilesCompiledResult.Success` carries no diagnostics, so `tryCompileProject` has nowhere to put them and `failBuildOnErrors` would never see them.
1513
- **Real F# source maps.** `FileWriter.AddSourceMapping` in `src/Fable.Compiler/Library.fs:84-90` is a no-op with the `SourceMapSharp` generator commented out, so `CliArgs.SourceMaps` does nothing. The plugin returns `{ mappings: '' }`, which is honest about having no mapping, but a real F#-to-JS one needs the Fable change first. Not filed.
1614

1715
## 2. A Vite DevTools panel (on hold)

bun.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/how.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ sequenceDiagram
5757
Plugin->>Daemon: fable/project-changed
5858
Daemon-->>Plugin: source files, diagnostics, MSBuild inputs
5959
Plugin->>Daemon: fable/initial-compile
60-
Daemon-->>Plugin: JavaScript per F# file
60+
Daemon-->>Plugin: JavaScript per F# file, and what Fable could not translate
6161
Vite->>Plugin: load (per .fs request)
6262
</div>
6363

@@ -80,6 +80,18 @@ and type-checks the project. `fable/initial-compile` then transpiles every sourc
8080
inside the daemon on [Fable.Compiler](https://github.com/fable-compiler/Fable), the same code
8181
`dotnet fable` uses.
8282

83+
Both steps report problems, and they are not the same problems. The type-check reports what the F#
84+
compiler found, `FS0025` and the like: code that does not compile. The compile reports what Fable
85+
found, which is code that compiles as F# but that Fable cannot turn into JavaScript, such as
86+
`Async.RunSynchronously`. Those carry no error number, so the plugin names them by their tag:
87+
88+
```text
89+
9:42:26 PM [vite] [fable] ERROR FABLE: Microsoft.FSharp.Control.FSharpAsync.RunSynchronously (static) is not supported by Fable Math.fs (6,26) (6,48)
90+
```
91+
92+
Either kind fails `vite build`. In dev the server stays up and the error goes to the browser
93+
overlay, so the next edit can fix it.
94+
8395
The daemon also reports which MSBuild files the project depends on. The plugin watches those, so a
8496
change to an `fsproj` or a `Directory.Build.props` triggers a full re-crack rather than an
8597
incremental compile.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"sample-project"
88
],
99
"catalog": {
10-
"@fable-org/fable-library-js": "2.5.1",
10+
"@fable-org/fable-library-js": "2.6.0",
1111
"vite": "^8.2.2",
1212
"vite-plugin-inspect": "^12.0.2"
1313
}

packages/vite-plugin-fable/Fable.Daemon.Tests/DebugTests.fs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,25 @@ module WireTests =
439439
}
440440
Severity = "Warning"
441441
FileName = "/project/Math.fs"
442+
Tag = "FSHARP"
443+
}
444+
445+
/// What Fable reports about a file that type-checks but that it cannot translate. It carries no
446+
/// error number, which is why `ErrorNumberText` is empty rather than absent.
447+
let private fableDiagnostic : Diagnostic =
448+
{
449+
ErrorNumberText = ""
450+
Message = "Microsoft.FSharp.Control.FSharpAsync.RunSynchronously (static) is not supported by Fable"
451+
Range =
452+
{
453+
StartLine = 7
454+
StartColumn = 12
455+
EndLine = 7
456+
EndColumn = 41
457+
}
458+
Severity = "Error"
459+
FileName = "/project/Math.fs"
460+
Tag = "FABLE"
442461
}
443462

444463
[<Test>]
@@ -458,7 +477,10 @@ module WireTests =
458477
[<Test>]
459478
let ``fable/initial-compile matches its fixture`` () =
460479
let response =
461-
FilesCompiledResult.Success (Map.ofList [ "/project/Math.fs", "export const sum = 1;" ])
480+
FilesCompiledResult.Success (
481+
Map.ofList [ "/project/Math.fs", "export const sum = 1;" ],
482+
[| fableDiagnostic |]
483+
)
462484

463485
Assert.That (
464486
normalize (serialize response),
@@ -468,7 +490,10 @@ module WireTests =
468490
[<Test>]
469491
let ``fable/compile matches its fixture`` () =
470492
let response =
471-
FileChangedResult.Success (Map.ofList [ "/project/Math.fs", "export const sum = 2;" ], [| diagnostic |])
493+
FileChangedResult.Success (
494+
Map.ofList [ "/project/Math.fs", "export const sum = 2;" ],
495+
[| diagnostic ; fableDiagnostic |]
496+
)
472497

473498
Assert.That (normalize (serialize response), Is.EqualTo (normalize (File.ReadAllText (fixture "compile.json"))))
474499

@@ -529,6 +554,7 @@ module DebugServerTests =
529554
EndLine = 3
530555
EndColumn = 9
531556
}
557+
Tag = "FSHARP"
532558
Severity = "Warning"
533559
FileName = mathFs
534560
}
@@ -651,7 +677,33 @@ module DebugServerTests =
651677

652678
Assert.That (files.RootElement.GetProperty("files").[0].GetProperty("compiled").GetBoolean(), Is.False)
653679

654-
Debug.publishInitialCompile (Map.ofList [ mathFs, "export const sum = 1;" ]) Set.empty
680+
// A file that type-checks but that Fable cannot translate reports nothing to the
681+
// type-check, so the compile is the only place its error can come from.
682+
let fableError : Diagnostic =
683+
{
684+
ErrorNumberText = ""
685+
Message = "Microsoft.FSharp.Control.FSharpAsync.RunSynchronously (static) is not supported by Fable"
686+
Range =
687+
{
688+
StartLine = 7
689+
StartColumn = 12
690+
EndLine = 7
691+
EndColumn = 41
692+
}
693+
Severity = "Error"
694+
FileName = mathFs
695+
Tag = "FABLE"
696+
}
697+
698+
Debug.publishInitialCompile (Map.ofList [ mathFs, "export const sum = 1;" ]) Set.empty [| fableError |]
699+
700+
use! compileErrors = getJson client $"{baseUrl}/api/diagnostics?severity=error"
701+
Assert.That (compileErrors.RootElement.GetProperty("count").GetInt32(), Is.EqualTo 1)
702+
703+
Assert.That (
704+
compileErrors.RootElement.GetProperty("diagnostics").[0].GetProperty("source").GetString(),
705+
Is.EqualTo "compile"
706+
)
655707

656708
use! compiled = getJson client $"{baseUrl}/api/files?path=Math.fs"
657709

packages/vite-plugin-fable/Fable.Daemon/Debug.fs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,15 +212,20 @@ let publishProject (project : ProjectState) : unit =
212212
}
213213
)
214214

215-
let publishInitialCompile (compiled : Map<FullPath, JavaScript>) (fromCache : Set<FullPath>) : unit =
215+
let publishInitialCompile
216+
(compiled : Map<FullPath, JavaScript>)
217+
(fromCache : Set<FullPath>)
218+
(diagnostics : Diagnostic array)
219+
: unit
220+
=
216221
advance (fun s ->
217222
{ s with
218223
Compile =
219224
Some
220225
{
221226
CompiledFiles = compiled
222227
FromCache = fromCache
223-
Diagnostics = Array.empty
228+
Diagnostics = diagnostics
224229
LastRequested = Array.empty
225230
LastCompiledAt = DateTime.Now
226231
}

packages/vite-plugin-fable/Fable.Daemon/Debug.fsi

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,10 @@ val isEnabled : unit -> bool
5151
/// decides which files the project even has.
5252
val publishProject : project : ProjectState -> unit
5353

54-
/// Record what a full project compile produced.
55-
val publishInitialCompile : compiled : Map<FullPath, JavaScript> -> fromCache : Set<FullPath> -> unit
54+
/// Record what a full project compile produced. The diagnostics are Fable's own; the F# ones
55+
/// belong to the crack that came before and are reported there.
56+
val publishInitialCompile :
57+
compiled : Map<FullPath, JavaScript> -> fromCache : Set<FullPath> -> diagnostics : Diagnostic array -> unit
5658

5759
/// Record what compiling a set of changed files produced, merged over what was compiled before,
5860
/// the way the plugin merges it into its own map.

0 commit comments

Comments
 (0)