-
Notifications
You must be signed in to change notification settings - Fork 300
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
Avoid unnecessary updates #2869
Comments
Makes sense. Funny enough, I had similar issues when working with hot update for Fable.Lit but tried to fix it in a more complicated way using the triggeredByDependency flag. However, we need to be a bit careful with this to avoid performance hits. The Fable printer is designed to write directly into the file stream so I assume we need to:
|
+1
IMO, as pointed in the link:
IMO it would be easier to always use memory stream to simplify the file handling logic, instead of forking it. |
This makes sense @ncave. As you say, if we can do something optimized enough, it's probably better to replace the current method instead of having two ways of writing the file. Comparing the bytes length should resolve equality in 99% of cases. Will the file size correspond exactly with the byte length for the memory stream (not sure if there's any extra byte saved for the encoding depending on the system)? I was also thinking that it was better for parallelization to write asynchronously and directly to the file stream, but maybe it's more efficient to use a memory stream and flush the whole contents to file at once. I'm currently focused on preparing a prerelease of |
In any case @MangelMaxime, do you think it would be possible to use the |
In my case, this is not possible to use the Indeed, in the project, that I am working on we decided to use Fable because we like to use F# to write the code but we made sure that the generated JavaScript would be the "same" as the one that we would write in JavaScript. This has a huge benefit which is any JavaScript tools that exist, we can use it in our project without any hacks or bad surprise. So for example, in the case of HMR with Vite and React-refresh, we do nothing to make it work. We just write our code with the same convention as the JavaScript one:
Personally, I feel like In theory, when writing JavaScript manually there is only one way to have mutiple save occuring at the same time which is if use your IDE feature "Save all", but I think in general people use Personnally, before digging into this issue I was thinking that if I edit Even, if the gain of performance is not huge this should still increase the performance or at least the feedback loop because writing to the disk is "slow", but also because then HMR would just need to update one file and not detecting and handling all the changes in browser etc. |
As I'm still busy with Fable 4, it'd be great if some contributor could handle this. It's a good first issue because it doesn't require specific knowledge about Fable, only how to use streams in .NET. Basically we need to do two things:
|
@alfonsogarciacaro, if nobody is working on this I would like to do it as my first issue. In fact, I was already trying out yesterday and here is what I have so far:
So that's what I have at the moment, could you give me some guidance on how to properly progress on the second step? Also, feel free to tell me if you feel this might be to advanced for me at the moment. |
Sorry for the late reply and thanks a lot for your help @hensou! If you open a PR it may be easier to review your current work as we can have a look at the code.
Yes, that sounds about right👍
You can expose the |
Thanks @alfonsogarciacaro, I'll work on that and submit a PR for review. |
You could keep the |
@inosik indeed, I think that would be a better thing to do. I'll go for that. |
- Keep output in memory - Only write to file if there is a change or file is new Fix fable-compiler#2869
@alfonsogarciacaro, I think with PR #2941 I've managed to fix this issue. |
- Keep output in memory - Only write to file if there is a change or file is new Fix #2869
Fantastic work @hensou, thanks a lot for your help and welcome to the contributor's team! 👏 🏅 👏 @MangelMaxime This has been released in 3.7.15, please give it a try when you have a moment, and let us know if it's working for you. |
Many thanks, confirming this greatly reduces the amount of updates (and screen flickering because of theme refreshes) editing a common files produces. However, I still do get an extra update or two. Turns out it's because those files really are different, but the only change is: @alfonsogarciacaro, I'm assuming these labels are non-deterministic because of parallel processing? |
Thanks a lot for checking @kerams! Good to know it's (mostly) working for you. Interesting about It looks like the F# compiler has a counter for name deduplication that is not reset for recompilations, thus giving higher values each time. It's strange because other auto-generated identifiers like |
Yes, it seems to be this: https://github.com/dotnet/fsharp/blob/d6aaefba78df55514178a5f49c44b44595a8f3ef/src/Compiler/Checking/PatternMatchCompilation.fs#L1279 I can think of a couple of solutions:
|
Thanks @alfonsogarciacaro and everyone, I'm looking forward to contributing more 😄 |
For me Fable 3.7.15, still updates the files that depends on it. Could it be related to the fact that It contains stuff like that //------------------------------------------------------------------------------
// This code was generated by fable-css-modules.
// Changes to this file will be lost when the code is regenerated.
//------------------------------------------------------------------------------
[<RequireQualifiedAccess>]
module internal CssModules
open Fable.Core
module Components =
type Consent =
/// <summary>
/// Binding for <c>page-content</c> class
/// </summary>
[<Emit("$0[\"page-content\"]")>]
abstract pageContent : string
/// <summary>
/// Binding for <c>label</c> class
/// </summary>
[<Emit("$0[\"label\"]")>]
abstract label : string Meaning that each file contains a line like: let private classes : CssModules.Components.Consent = import "default" "./Consent.module.scss" In order to access the binding definition. I will look to make a reproduction repo |
I tried to update a "normal" file and in that case only that files seems to be rewritten. I don't remember if this was the case before or not. |
@MangelMaxime Do you mean CssModules.fs is a dependent file and it's being updated (the last modified timestamp changes) even if there are no changes? Can you check if there's been an actual change like the name of a generated variable as in the case reported by @kerams? There are several rules for how Fable builds the watch dependency tree. One way to test it is:
|
I had a hard time making a reproduction because when scaffolding a project from scratch I was not able to reproduce. So I trimmed a lot of stuff from a production application and it seems to be reproducible even outside of my computer (tested on Gitpod). If you look at the following picture, you can see that The generated file import Consent$002Emodule from "./Consent.module.css";
import { createElement } from "react";
import { createObj } from "../fable_modules/fable-library.3.7.15/Util.js";
import { Interop_reactApi } from "../fable_modules/Feliz.1.64.0/./Interop.fs.js";
import { ofArray } from "../fable_modules/fable-library.3.7.15/List.js";
import React from "react";
const classes = Consent$002Emodule;
export function Consent() {
let elems;
return createElement("div", createObj(ofArray([["className", classes["page-content"]], (elems = [createElement("div", {
children: ["Consent component"],
})], ["children", Interop_reactApi.Children.toArray(Array.from(elems))])])));
}
//# sourceMappingURL=Consent.fs.js.map The generated fable_repro_bug.mp4You can find a reproduction repository here: https://github.com/MangelMaxime/fable-2869-reproduction |
Thanks @MangelMaxime for the reproduction repo. I can further investigate this later today, but I think this line (248) might be the issue. Lines 241 to 251 in e54f2b1
If we always append the sourceMap info after we already saved to the file, then when we compare memory stream with the file, they will always be different since the memory stream does not have Does it make sense? |
@hensou I think you are onto something indeed. Because, when scaffolding the reproduction from a template of mine I don't have the source maps enable. |
@MangelMaxime yep, we can isolate the problem even more as if we remove the dependency from I've made some changes to write the |
I've pushed 3.7.16, this includes @hensou fix to prevent updating files with source maps when not necessary and also a fix to make compiler-generated variable names deterministic (basically by removing the numeric suffix added by FCS and default to Fable's deduplicate mechanism). @MangelMaxime @kerams Could you please give it a try when you have a moment and confirm if this fixed your issues? Thanks a lot! |
Awesome, the numbers are removed and so are the extra updates, thanks. Just curious, is it possible this could cause regressions in the future? |
Awesome, thanks for confirming @kerams! Well, you never know, but I assume this won't be a problem, because FCS was inconsistent with this numeric suffixes ( |
Thank you all, I confirm that I can no longer reproduce the problem with my last reproduction code. 👍 |
UPDATE: This is a good first issue for contributors as it doesn't require specific knowledge about Fable code. See the steps necessary to fix in this comment below.
This is a behaviour I detected after reporting #2868
Description
Let say that in my project I have the following files:
CssModules.fs
App.fs
Login.fs
Navbar.fs
and
CssModules.fs
is referenced in all the other files.When
CssModules.fs
change Fable is recompiling all the files that depends on it and rewrite them on the disk even if nothing changed in the output of each of these files.This cause a lot unnecessary "hot update" to be triggered by the bundlers. I think Fable should only write a file on the disk if the output of the compilation for that file changed since the last compilation.
Related information
3.7.5
The text was updated successfully, but these errors were encountered: