git.iko.soy/lynxjs-elm/compiler
on master help sign in
Automatically invalidate project cache when embedded packages change
commit94127db6715c5131e9d18051c61a477475b53a61 authoriko <mail@iko.soy> dateFri 20 Mar 2026, 03:36 (6 months ago) parent51621f2
7 files changed+44−15
CLAUDE.md+1−1 builder/src/Build.hs+3−3 builder/src/Elm/Details.hs+9−7 builder/src/Lynx/Patches.hs+27 terminal/src/Develop/Generate/Index.hs+1−1 terminal/src/Make.hs+1−1 terminal/src/Publish.hs+2−2
CLAUDE.md+1−1
@@ -41,7 +41,7 @@ examples/ # Demo apps (counter, hello-world, http-example)
4141
4242`packages/` contains forked elm/virtual-dom, elm/browser, elm/http, and the custom lynx/ui package. Upstream repos are at `github.com/lynxjs-elm/{virtual-dom,browser,http,ui}`. Their source is embedded into the compiler binary at build time via `file-embed` in `builder/src/Lynx/Patches.hs`. When a project depends on these packages, the compiler installs them from embedded content instead of downloading from package.elm-lang.org. The `lynx/ui` package is also injected into the package registry so the dependency solver can resolve it.
4343
44−After editing a fork, rebuild the compiler (`stack build && stack install`) and clear `~/.lynxjs-elm` to pick up changes.
44+After editing a fork, rebuild the compiler (`stack build && stack install`). The compiler automatically detects embedded content changes and invalidates caches.
4545
4646## Kernel JS rules
4747
builder/src/Build.hs+3−3
@@ -75,7 +75,7 @@ data Env =
7575
7676
7777makeEnv :: Reporting.BKey -> FilePath -> Details.Details -> IO Env
78−makeEnv key root (Details.Details _ validOutline buildID locals foreigns _) =
78+makeEnv key root (Details.Details _ _ validOutline buildID locals foreigns _) =
7979 case validOutline of
8080 Details.ValidApp givenSrcDirs ->
8181 do srcDirs <- traverse (toAbsoluteSrcDir root) (NE.toList givenSrcDirs)
@@ -749,9 +749,9 @@ projectTypeToPkg projectType =
749749
750750
751751writeDetails :: FilePath -> Details.Details -> Map.Map ModuleName.Raw Result -> IO ()
752−writeDetails root (Details.Details time outline buildID locals foreigns extras) results =
752+writeDetails root (Details.Details time fh outline buildID locals foreigns extras) results =
753753 File.writeBinary (Stuff.details root) $
754− Details.Details time outline buildID (Map.foldrWithKey addNewLocal locals results) foreigns extras
754+ Details.Details time fh outline buildID (Map.foldrWithKey addNewLocal locals results) foreigns extras
755755
756756
757757addNewLocal :: ModuleName.Raw -> Result -> Map.Map ModuleName.Raw Details.Local -> Map.Map ModuleName.Raw Details.Local
builder/src/Elm/Details.hs+9−7
@@ -67,6 +67,7 @@ import qualified Stuff
6767data Details =
6868 Details
6969 { _outlineTime :: File.Time
70+ , _forkHash :: Word64
7071 , _outline :: ValidOutline
7172 , _buildID :: BuildID
7273 , _locals :: Map.Map ModuleName.Raw Local
@@ -125,14 +126,14 @@ type Interfaces =
125126
126127
127128loadObjects :: FilePath -> Details -> IO (MVar (Maybe Opt.GlobalGraph))
128−loadObjects root (Details _ _ _ _ _ extras) =
129+loadObjects root (Details _ _ _ _ _ _ extras) =
129130 case extras of
130131 ArtifactsFresh _ o -> newMVar (Just o)
131132 ArtifactsCached -> fork (File.readBinary (Stuff.objects root))
132133
133134
134135loadInterfaces :: FilePath -> Details -> IO (MVar (Maybe Interfaces))
135−loadInterfaces root (Details _ _ _ _ _ extras) =
136+loadInterfaces root (Details _ _ _ _ _ _ extras) =
136137 case extras of
137138 ArtifactsFresh i _ -> newMVar (Just i)
138139 ArtifactsCached -> fork (File.readBinary (Stuff.interfaces root))
@@ -164,8 +165,8 @@ load style scope root =
164165 Nothing ->
165166 generate style scope root newTime
166167
167− Just details@(Details oldTime _ buildID _ _ _) ->
168− if oldTime == newTime
168+ Just details@(Details oldTime oldHash _ buildID _ _ _) ->
169+ if oldTime == newTime && oldHash == Lynx.forkContentHash
169170 then return (Right details { _buildID = buildID + 1 })
170171 else generate style scope root newTime
171172
@@ -331,7 +332,7 @@ verifyDependencies env@(Env key scope root cache _ _ _) time outline solution di
331332 objs = Map.foldr addObjects Opt.empty artifacts
332333 ifaces = Map.foldrWithKey (addInterfaces directDeps) Map.empty artifacts
333334 foreigns = Map.map (OneOrMore.destruct Foreign) $ Map.foldrWithKey gatherForeigns Map.empty $ Map.intersection artifacts directDeps
334− details = Details time outline 0 Map.empty foreigns (ArtifactsFresh ifaces objs)
335+ details = Details time Lynx.forkContentHash outline 0 Map.empty foreigns (ArtifactsFresh ifaces objs)
335336 in
336337 do BW.writeBinary scope (Stuff.objects root) objs
337338 BW.writeBinary scope (Stuff.interfaces root) ifaces
@@ -789,14 +790,15 @@ endpointDecoder =
789790
790791
791792instance Binary Details where
792− put (Details a b c d e _) = put a >> put b >> put c >> put d >> put e
793+ put (Details a b c d e f _) = put a >> put b >> put c >> put d >> put e >> put f
793794 get =
794795 do a <- get
795796 b <- get
796797 c <- get
797798 d <- get
798799 e <- get
799− return (Details a b c d e ArtifactsCached)
800+ f <- get
801+ return (Details a b c d e f ArtifactsCached)
800802
801803
802804instance Binary ValidOutline where
builder/src/Lynx/Patches.hs+27
@@ -4,6 +4,7 @@ module Lynx.Patches
44 , installAllForks
55 , isFork
66 , injectRegistry
7+ , forkContentHash
78 )
89 where
910
@@ -11,6 +12,7 @@ module Lynx.Patches
1112import qualified Data.ByteString as BS
1213import Data.FileEmbed (embedDir, embedFile)
1314import qualified Data.Map as Map
15+import Data.Word (Word64)
1416import qualified System.Directory as Dir
1517import System.FilePath ((</>), takeDirectory)
1618
@@ -63,6 +65,31 @@ cryptoElmJson = $(embedFile "packages/crypto/elm.json")
6365
6466
6567
68+-- FORK CONTENT HASH
69+--
70+-- A hash of all embedded package content, used to invalidate the project
71+-- cache (elm-stuff) when the compiler is rebuilt with updated packages.
72+
73+
74+forkContentHash :: Word64
75+forkContentHash =
76+ let
77+ hashBS :: Word64 -> BS.ByteString -> Word64
78+ hashBS acc bs = BS.foldl' (\h b -> h * 31 + fromIntegral b) acc bs
79+
80+ allBytes :: [BS.ByteString]
81+ allBytes = concat
82+ [ virtualDomElmJson : map snd virtualDomSrc
83+ , browserElmJson : map snd browserSrc
84+ , httpElmJson : map snd httpSrc
85+ , uiElmJson : map snd uiSrc
86+ , cryptoElmJson : map snd cryptoSrc
87+ ]
88+ in
89+ foldl' hashBS 0 allBytes
90+
91+
92+
6693-- IS FORK
6794
6895
terminal/src/Develop/Generate/Index.hs+1−1
@@ -173,7 +173,7 @@ getExactDeps maybeOutline =
173173 Left _ ->
174174 return Map.empty
175175
176− Right (Details.Details _ validOutline _ _ _ _) ->
176+ Right (Details.Details _ _ validOutline _ _ _ _) ->
177177 case validOutline of
178178 Details.ValidApp _ ->
179179 return Map.empty
terminal/src/Make.hs+1−1
@@ -149,7 +149,7 @@ getMode debug optimize =
149149
150150
151151getExposed :: Details.Details -> Task (NE.List ModuleName.Raw)
152−getExposed (Details.Details _ validOutline _ _ _ _) =
152+getExposed (Details.Details _ _ validOutline _ _ _ _) =
153153 case validOutline of
154154 Details.ValidApp _ ->
155155 Task.throw Exit.MakeAppNeedsFileNames
terminal/src/Publish.hs+2−2
@@ -173,7 +173,7 @@ verifyBuild :: FilePath -> Task.Task Exit.Publish Docs.Documentation
173173verifyBuild root =
174174 reportBuildCheck $ BW.withScope $ \scope ->
175175 Task.run $
176− do details@(Details.Details _ outline _ _ _ _) <-
176+ do details@(Details.Details _ _ outline _ _ _ _) <-
177177 Task.eio Exit.PublishBadDetails $
178178 Details.load Reporting.silent scope root
179179
@@ -309,7 +309,7 @@ withPrepublishDir root callback =
309309verifyZipBuild :: FilePath -> IO (Either Exit.Publish ())
310310verifyZipBuild root =
311311 BW.withScope $ \scope -> Task.run $
312− do details@(Details.Details _ outline _ _ _ _) <-
312+ do details@(Details.Details _ _ outline _ _ _ _) <-
313313 Task.eio Exit.PublishZipBadDetails $
314314 Details.load Reporting.silent scope root
315315