git.iko.soy/lynxjs-elm/compiler
on master help sign in
Automatically invalidate project cache when embedded packages change
94127dbiko
CLAUDE.md+1−1
@@ -41,7 +41,7 @@ examples/ # Demo apps (counter, hello-world, http-example)
41
41
42`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.
42`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.
43
43
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.
45
45
46## Kernel JS rules
46## Kernel JS rules
47
47
builder/src/Build.hs+3−3
@@ -75,7 +75,7 @@ data Env =
75
75
76
76
77makeEnv :: Reporting.BKey -> FilePath -> Details.Details -> IO Env
77makeEnv :: 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 _) =
79 case validOutline of
79 case validOutline of
80 Details.ValidApp givenSrcDirs ->
80 Details.ValidApp givenSrcDirs ->
81 do srcDirs <- traverse (toAbsoluteSrcDir root) (NE.toList givenSrcDirs)
81 do srcDirs <- traverse (toAbsoluteSrcDir root) (NE.toList givenSrcDirs)
@@ -749,9 +749,9 @@ projectTypeToPkg projectType =
749
749
750
750
751writeDetails :: FilePath -> Details.Details -> Map.Map ModuleName.Raw Result -> IO ()
751writeDetails :: 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 =
753 File.writeBinary (Stuff.details root) $
753 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
755
755
756
756
757addNewLocal :: ModuleName.Raw -> Result -> Map.Map ModuleName.Raw Details.Local -> Map.Map ModuleName.Raw Details.Local
757addNewLocal :: 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
67data Details =
67data Details =
68 Details
68 Details
69 { _outlineTime :: File.Time
69 { _outlineTime :: File.Time
70+ , _forkHash :: Word64
70 , _outline :: ValidOutline
71 , _outline :: ValidOutline
71 , _buildID :: BuildID
72 , _buildID :: BuildID
72 , _locals :: Map.Map ModuleName.Raw Local
73 , _locals :: Map.Map ModuleName.Raw Local
@@ -125,14 +126,14 @@ type Interfaces =
125
126
126
127
127loadObjects :: FilePath -> Details -> IO (MVar (Maybe Opt.GlobalGraph))
128loadObjects :: FilePath -> Details -> IO (MVar (Maybe Opt.GlobalGraph))
128−loadObjects root (Details _ _ _ _ _ extras) =
129+loadObjects root (Details _ _ _ _ _ _ extras) =
129 case extras of
130 case extras of
130 ArtifactsFresh _ o -> newMVar (Just o)
131 ArtifactsFresh _ o -> newMVar (Just o)
131 ArtifactsCached -> fork (File.readBinary (Stuff.objects root))
132 ArtifactsCached -> fork (File.readBinary (Stuff.objects root))
132
133
133
134
134loadInterfaces :: FilePath -> Details -> IO (MVar (Maybe Interfaces))
135loadInterfaces :: FilePath -> Details -> IO (MVar (Maybe Interfaces))
135−loadInterfaces root (Details _ _ _ _ _ extras) =
136+loadInterfaces root (Details _ _ _ _ _ _ extras) =
136 case extras of
137 case extras of
137 ArtifactsFresh i _ -> newMVar (Just i)
138 ArtifactsFresh i _ -> newMVar (Just i)
138 ArtifactsCached -> fork (File.readBinary (Stuff.interfaces root))
139 ArtifactsCached -> fork (File.readBinary (Stuff.interfaces root))
@@ -164,8 +165,8 @@ load style scope root =
164 Nothing ->
165 Nothing ->
165 generate style scope root newTime
166 generate style scope root newTime
166
167
167− Just details@(Details oldTime _ buildID _ _ _) ->
168+ Just details@(Details oldTime oldHash _ buildID _ _ _) ->
168− if oldTime == newTime
169+ if oldTime == newTime && oldHash == Lynx.forkContentHash
169 then return (Right details { _buildID = buildID + 1 })
170 then return (Right details { _buildID = buildID + 1 })
170 else generate style scope root newTime
171 else generate style scope root newTime
171
172
@@ -331,7 +332,7 @@ verifyDependencies env@(Env key scope root cache _ _ _) time outline solution di
331 objs = Map.foldr addObjects Opt.empty artifacts
332 objs = Map.foldr addObjects Opt.empty artifacts
332 ifaces = Map.foldrWithKey (addInterfaces directDeps) Map.empty artifacts
333 ifaces = Map.foldrWithKey (addInterfaces directDeps) Map.empty artifacts
333 foreigns = Map.map (OneOrMore.destruct Foreign) $ Map.foldrWithKey gatherForeigns Map.empty $ Map.intersection artifacts directDeps
334 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)
335 in
336 in
336 do BW.writeBinary scope (Stuff.objects root) objs
337 do BW.writeBinary scope (Stuff.objects root) objs
337 BW.writeBinary scope (Stuff.interfaces root) ifaces
338 BW.writeBinary scope (Stuff.interfaces root) ifaces
@@ -789,14 +790,15 @@ endpointDecoder =
789
790
790
791
791instance Binary Details where
792instance 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
793 get =
794 get =
794 do a <- get
795 do a <- get
795 b <- get
796 b <- get
796 c <- get
797 c <- get
797 d <- get
798 d <- get
798 e <- get
799 e <- get
800+ f <- get
799− return (Details a b c d e ArtifactsCached)
801+ return (Details a b c d e f ArtifactsCached)
800
802
801
803
802instance Binary ValidOutline where
804instance Binary ValidOutline where
builder/src/Lynx/Patches.hs+27
@@ -4,6 +4,7 @@ module Lynx.Patches
4 , installAllForks
4 , installAllForks
5 , isFork
5 , isFork
6 , injectRegistry
6 , injectRegistry
7+ , forkContentHash
7 )
8 )
8 where
9 where
9
10
@@ -11,6 +12,7 @@ module Lynx.Patches
11import qualified Data.ByteString as BS
12import qualified Data.ByteString as BS
12import Data.FileEmbed (embedDir, embedFile)
13import Data.FileEmbed (embedDir, embedFile)
13import qualified Data.Map as Map
14import qualified Data.Map as Map
15+import Data.Word (Word64)
14import qualified System.Directory as Dir
16import qualified System.Directory as Dir
15import System.FilePath ((</>), takeDirectory)
17import System.FilePath ((</>), takeDirectory)
16
18
@@ -63,6 +65,31 @@ cryptoElmJson = $(embedFile "packages/crypto/elm.json")
63
65
64
66
65
67
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+
66-- IS FORK
93-- IS FORK
67
94
68
95
terminal/src/Develop/Generate/Index.hs+1−1
@@ -173,7 +173,7 @@ getExactDeps maybeOutline =
173 Left _ ->
173 Left _ ->
174 return Map.empty
174 return Map.empty
175
175
176− Right (Details.Details _ validOutline _ _ _ _) ->
176+ Right (Details.Details _ _ validOutline _ _ _ _) ->
177 case validOutline of
177 case validOutline of
178 Details.ValidApp _ ->
178 Details.ValidApp _ ->
179 return Map.empty
179 return Map.empty
terminal/src/Make.hs+1−1
@@ -149,7 +149,7 @@ getMode debug optimize =
149
149
150
150
151getExposed :: Details.Details -> Task (NE.List ModuleName.Raw)
151getExposed :: Details.Details -> Task (NE.List ModuleName.Raw)
152−getExposed (Details.Details _ validOutline _ _ _ _) =
152+getExposed (Details.Details _ _ validOutline _ _ _ _) =
153 case validOutline of
153 case validOutline of
154 Details.ValidApp _ ->
154 Details.ValidApp _ ->
155 Task.throw Exit.MakeAppNeedsFileNames
155 Task.throw Exit.MakeAppNeedsFileNames
terminal/src/Publish.hs+2−2
@@ -173,7 +173,7 @@ verifyBuild :: FilePath -> Task.Task Exit.Publish Docs.Documentation
173verifyBuild root =
173verifyBuild root =
174 reportBuildCheck $ BW.withScope $ \scope ->
174 reportBuildCheck $ BW.withScope $ \scope ->
175 Task.run $
175 Task.run $
176− do details@(Details.Details _ outline _ _ _ _) <-
176+ do details@(Details.Details _ _ outline _ _ _ _) <-
177 Task.eio Exit.PublishBadDetails $
177 Task.eio Exit.PublishBadDetails $
178 Details.load Reporting.silent scope root
178 Details.load Reporting.silent scope root
179
179
@@ -309,7 +309,7 @@ withPrepublishDir root callback =
309verifyZipBuild :: FilePath -> IO (Either Exit.Publish ())
309verifyZipBuild :: FilePath -> IO (Either Exit.Publish ())
310verifyZipBuild root =
310verifyZipBuild root =
311 BW.withScope $ \scope -> Task.run $
311 BW.withScope $ \scope -> Task.run $
312− do details@(Details.Details _ outline _ _ _ _) <-
312+ do details@(Details.Details _ _ outline _ _ _ _) <-
313 Task.eio Exit.PublishZipBadDetails $
313 Task.eio Exit.PublishZipBadDetails $
314 Details.load Reporting.silent scope root
314 Details.load Reporting.silent scope root
315
315