on master help sign in
14 files changed+73−311
since splitsnapshots
unifiedsplit
ChangeLog.md−9 default.nix+4−10 lib/asset/README.md+2−2 lib/asset/manifest/obelisk-asset-manifest.cabal+1−11 lib/asset/manifest/src-bin/static-th.hs−41 lib/asset/manifest/src/Obelisk/Asset/Cabal.hs−42 lib/asset/manifest/src/Obelisk/Asset/Gather.hs+13−13 lib/asset/manifest/src/Obelisk/Asset/Promoted.hs+27−8 lib/asset/manifest/src/Obelisk/Asset/TH.hs+3−25 lib/cliapp/src/Obelisk/CliApp/Logging.hs+1−5 lib/command/obelisk-command.cabal−3 lib/command/src/Obelisk/Command/Project.hs+8−114 lib/command/src/Obelisk/Command/Run.hs+10−25 skeleton/frontend/src/Frontend.hs+4−3
ChangeLog.md−9
@@ -2,15 +2,6 @@
22
33This project's release branch is `master`. This log is written from the perspective of the release branch: when changes hit `master`, they are considered released.
44
5−## Unreleased
6−
7−* Use TemplateHaskell to determine asset file paths
8− * Migration: All uses of `static @"some/path"` become `$(static "some/path")`. Instead of requiring `TypeApplications` and `DataKinds`, modules calling `static` must now enable `TemplateHaskell`.
9− * Deprecation: Deprecate static asset modules generated via 'obelisk-asset-manifest-generate' in favor of modules generated via 'obelisk-asset-th-generate'. The new executable takes the same arguments as the old and should be a drop-in replacement. To preserve the old behavior, set `__deprecated.useObeliskAssetManifestGenerate = true;` in your obelisk project configuration.
10− * Feature: Files added to the static directory while `ob run` is active no longer require `ob run` to be restarted
11−* Feature: When `staticFiles` is a derivation, as opposed to a regular directory, produce a symlink to the result of that derivation at `static.out` and have `ob run` serve static assets from that symlink. This makes is possible for the static asset derivation to be rebuilt and the new results served without restarting `ob run`.
12−* Feature: Rebuild static asset derivations while `ob run` is active as long as the change to the derivation is within the project folder. `ob run` now displays a message ("Static assets rebuilt and symlinked to static.out") whenever static assets have been rebuilt and the new static assets are being served.
13−
145## v0.9.1.0
156
167* [#801](https://github.com/obsidiansystems/obelisk/pull/801): Remove errors and warning for local packages without a library component
default.nix+4−10
@@ -65,7 +65,7 @@ in rec {
6565 '';
6666 nullIfAbsent = p: if lib.pathExists p then p else null;
6767 #TODO: Avoid copying files within the nix store. Right now, obelisk-asset-manifest-generate copies files into a big blob so that the android/ios static assets can be imported from there; instead, we should get everything lined up right before turning it into an APK, so that copies, if necessary, only exist temporarily.
68− processAssets = { src, packageName ? "obelisk-generated-static", moduleName ? "Obelisk.Generated.Static", exe ? "obelisk-asset-th-generate" }: pkgs.runCommand "asset-manifest" {
68+ processAssets = { src, packageName ? "obelisk-generated-static", moduleName ? "Obelisk.Generated.Static" }: pkgs.runCommand "asset-manifest" {
6969 inherit src;
7070 outputs = [ "out" "haskellManifest" "symlinked" ];
7171 nativeBuildInputs = [ ghcObelisk.obelisk-asset-manifest ];
@@ -73,7 +73,7 @@ in rec {
7373 set -euo pipefail
7474 touch "$out"
7575 mkdir -p "$symlinked"
76− ${exe} "$src" "$haskellManifest" ${packageName} ${moduleName} "$symlinked"
76+ obelisk-asset-manifest-generate "$src" "$haskellManifest" ${packageName} ${moduleName} "$symlinked"
7777 '';
7878
7979 compressedJs = frontend: optimizationLevel: pkgs.runCommand "compressedJs" {} ''
@@ -237,14 +237,13 @@ in rec {
237237 , withHoogle ? false # Setting this to `true` makes shell reloading far slower
238238 , __closureCompilerOptimizationLevel ? "ADVANCED" # Set this to `null` to skip the closure-compiler step
239239 , __withGhcide ? false
240− , __deprecated ? {}
241240 }:
242241 let
243242 allConfig = nixpkgs.lib.makeExtensible (self: {
244243 base = base';
245244 inherit args;
246245 userSettings = {
247− inherit android ios packages overrides tools shellToolOverrides withHoogle __closureCompilerOptimizationLevel __withGhcide __deprecated;
246+ inherit android ios packages overrides tools shellToolOverrides withHoogle __closureCompilerOptimizationLevel __withGhcide;
248247 staticFiles = if staticFiles == null then self.base + /static else staticFiles;
249248 };
250249 frontendName = "frontend";
@@ -252,12 +251,7 @@ in rec {
252251 commonName = "common";
253252 staticName = "obelisk-generated-static";
254253 staticFilesImpure = let fs = self.userSettings.staticFiles; in if lib.isDerivation fs then fs else toString fs;
255− processedStatic = processAssets {
256− src = self.userSettings.staticFiles;
257− exe = if lib.attrByPath ["userSettings" "__deprecated" "useObeliskAssetManifestGenerate"] false self
258− then builtins.trace "obelisk-asset-manifest-generate is deprecated. Use obelisk-asset-th-generate instead." "obelisk-asset-manifest-generate"
259− else "obelisk-asset-th-generate";
260− };
254+ processedStatic = processAssets { src = self.userSettings.staticFiles; };
261255 # The packages whose names and roles are defined by this package
262256 predefinedPackages = lib.filterAttrs (_: x: x != null) {
263257 ${self.frontendName} = nullIfAbsent (self.base + "/frontend");
lib/asset/README.md+2−2
@@ -5,7 +5,7 @@
55
66**Efficiently served, aggressively cached static web resources.**
77
8−The `assets.nix` file contains nix expressions that are a mutually recursive set of attributes used to create static asset directories with hashable file encodings. This file should be incorporated within your project's `default.nix` file.
8+The `assets.nix` file contains nix expressions that are a mutually recursive set of attributes used to create static asset directories with hashable file encodings. This file should be incorporated within your project's `default.nix` file. (See `example/default.nix`, line 2)
99
1010```nix
1111{ assets ? import ./../assets.nix { inherit nixpkgs; } }:
@@ -19,7 +19,7 @@ In this example, `mkAssets` is used on a directory that has a `.png` file inside
1919myAssets = assets.mkAssets ./static;
2020```
2121
22−Once you have successfully incorporated `assets.nix` into your project's nix file(s), use `nix-build` to generate a symlink of your `mkAssets` expression. The following command will generate an immutable symlink that holds hashed static assets.
22+Once you have successfully incorporated `assets.nix` into your project's nix file(s), use `nix-build` to generate a symlink of your `mkAssets` expression. The following command from `example/run-example` (line 3) will generate an immutable symlink that holds hashed static assets.
2323
2424```bash
2525nix-build -o static.assets -A myAssets
lib/asset/manifest/obelisk-asset-manifest.cabal+1−11
@@ -27,7 +27,6 @@ library
2727 , unix-compat
2828 , vector
2929 exposed-modules:
30− Obelisk.Asset.Cabal
3130 Obelisk.Asset.Gather
3231 Obelisk.Asset.Promoted
3332 Obelisk.Asset.Symlink
@@ -36,7 +35,7 @@ library
3635 other-extensions: TemplateHaskell
3736 ghc-options:
3837 -Wall -Werror -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O2
39− -fno-warn-unused-do-bind -funbox-strict-fields -fprof-auto-calls
38+ -fno-warn-unused-do-bind -funbox-strict-fields -fprof-auto-calls
4039
4140executable obelisk-asset-manifest-generate
4241 hs-source-dirs: src-bin
@@ -45,12 +44,3 @@ executable obelisk-asset-manifest-generate
4544 base
4645 , obelisk-asset-manifest
4746 , text
48−
49−executable obelisk-asset-th-generate
50− hs-source-dirs: src-bin
51− main-is: static-th.hs
52− build-depends:
53− base
54− , obelisk-asset-manifest
55− , filepath
56− , text
lib/asset/manifest/src-bin/static-th.hsdeleted−41
@@ -1,41 +0,0 @@
1−import qualified Data.Text as T
2−import Obelisk.Asset.Cabal
3−import Obelisk.Asset.Gather
4−import Obelisk.Asset.Symlink
5−import System.Environment
6−import System.FilePath
7−
8−main :: IO ()
9−main = do
10− --TODO: Usage
11− [root, haskellTarget, packageName, moduleName, fileTarget] <- getArgs
12− paths <- gatherHashedPaths root
13− writeCabalProject haskellTarget $ SimplePkg
14− { _simplePkg_name = T.pack packageName
15− , _simplePkg_moduleName = T.pack moduleName
16− , _simplePkg_dependencies = map T.pack
17− [ "base"
18− , "obelisk-asset-manifest"
19− , "template-haskell"
20− ]
21− , _simplePkg_moduleContents = T.pack $ unlines
22− [ "{-# Language CPP #-}"
23− , "{-|"
24− , " Description:"
25− , " Automatically generated module that provides the 'static' TH function"
26− , " to generate paths to static assets."
27− , "-}"
28− , "module " <> moduleName <> " ( static ) where"
29− , ""
30− , "import Obelisk.Asset.TH"
31− , "import Language.Haskell.TH"
32− , ""
33− , "static :: FilePath -> Q Exp"
34− , "#ifdef OBELISK_ASSET_PASSTHRU"
35− , "static = staticAssetRaw"
36− , "#else"
37− , "static = staticAssetHashed " <> show root
38− , "#endif"
39− ]
40− }
41− copyAndSymlink paths root fileTarget
lib/asset/manifest/src/Obelisk/Asset/Cabal.hsdeleted−42
@@ -1,42 +0,0 @@
1−{-# Language OverloadedStrings #-}
2−module Obelisk.Asset.Cabal where
3−
4−import qualified Data.List as L
5−import Data.Text (Text)
6−import qualified Data.Text as T
7−import qualified Data.Text.IO as T
8−import System.Directory
9−import System.FilePath
10−
11−-- | A generated single-module package
12−data SimplePkg = SimplePkg
13− { _simplePkg_name :: Text
14− , _simplePkg_moduleName :: Text
15− , _simplePkg_moduleContents :: Text
16− , _simplePkg_dependencies :: [Text]
17− }
18−
19−cabalFile :: SimplePkg -> Text
20−cabalFile (SimplePkg packageName moduleName _ deps) = T.unlines
21− [ "name: " <> packageName
22− , "version: 0"
23− , "cabal-version: >= 1.2"
24− , "build-type: Simple"
25− , ""
26− , "library"
27− , " hs-source-dirs: src"
28− , " build-depends:"
29− , " " <> T.intercalate ", " deps
30− , " exposed-modules: " <> moduleName
31− ]
32−
33−writeCabalProject :: FilePath -> SimplePkg -> IO ()
34−writeCabalProject target pkg = do
35− createDirectoryIfMissing True target
36− T.writeFile (target </> T.unpack (_simplePkg_name pkg) <.> "cabal") $ cabalFile pkg
37− let moduleName = _simplePkg_moduleName pkg
38− (modName', moduleDirPath) = case L.uncons (reverse $ T.splitOn "." moduleName) of
39− Nothing -> error $ "writeStaticProject: invalid module name " <> T.unpack moduleName
40− Just (name, parents) -> (name, target </> "src" </> T.unpack (T.intercalate "/" $ reverse parents))
41− createDirectoryIfMissing True moduleDirPath
42− T.writeFile (moduleDirPath </> T.unpack modName' <.> "hs") $ _simplePkg_moduleContents pkg
lib/asset/manifest/src/Obelisk/Asset/Gather.hs+13−13
@@ -6,21 +6,21 @@ module Obelisk.Asset.Gather
66 , toHashedPath
77 ) where
88
9−import Control.DeepSeq (force)
9+import Control.DeepSeq
1010import Control.Monad (forM)
11−import Data.Bits (shift, (.|.), (.&.))
12−import qualified Data.ByteString.Lazy as LBS (readFile, toStrict, ByteString, length, index)
13−import qualified Data.ByteString.Lazy.Builder as LBS (toLazyByteString, word8)
14−import Data.Char (ord)
15−import Data.Digest.Pure.SHA (bytestringDigest, sha256)
11+import Data.Bits
12+import qualified Data.ByteString.Lazy as LBS
13+import qualified Data.ByteString.Lazy.Builder as LBS
14+import Data.Char
15+import Data.Digest.Pure.SHA
1616import Data.Map (Map)
17−import qualified Data.Map as Map (singleton)
18−import qualified Data.Text as T (unpack)
19−import Data.Text.Encoding (decodeUtf8)
20−import qualified Data.Vector.Unboxed as UV ((!), fromList)
21−import Data.Word (Word8)
22−import System.FilePath.Posix ((</>), splitFileName, normalise)
23−import System.Directory (listDirectory, doesFileExist)
17+import qualified Data.Map as Map
18+import qualified Data.Text as T
19+import Data.Text.Encoding
20+import qualified Data.Vector.Unboxed as UV
21+import Data.Word
22+import System.FilePath.Posix
23+import System.Directory
2424
2525-- | Given a path, recursively explore it, creating hashed paths for all files found
2626gatherHashedPaths
lib/asset/manifest/src/Obelisk/Asset/Promoted.hs+27−8
@@ -10,6 +10,7 @@ module Obelisk.Asset.Promoted
1010import Obelisk.Asset.Gather
1111
1212import Data.Foldable
13+import qualified Data.List as L
1314import Language.Haskell.TH (runQ, pprint)
1415import Language.Haskell.TH.Syntax hiding (lift)
1516import GHC.TypeLits
@@ -17,11 +18,12 @@ import Data.Sequence (Seq)
1718import qualified Data.Sequence as Seq
1819import Control.Monad.Trans.Writer
1920import System.FilePath
21+import System.Directory
2022import Data.Text (Text)
2123import qualified Data.Text as T
24+import qualified Data.Text.IO as T
2225import Data.Map (Map)
2326import qualified Data.Map as Map
24−import Obelisk.Asset.Cabal (writeCabalProject, SimplePkg(..))
2527
2628data StaticConfig = StaticConfig
2729 { _staticConfig_packageName :: Text --TODO: Better type
@@ -30,14 +32,31 @@ data StaticConfig = StaticConfig
3032
3133writeStaticProject :: Map FilePath FilePath -> FilePath -> StaticConfig -> IO ()
3234writeStaticProject paths target cfg = do
35+ createDirectoryIfMissing True target
36+ T.writeFile (target </> T.unpack (_staticConfig_packageName cfg) <.> "cabal") $ staticCabalFile cfg
3337 let modName = _staticConfig_moduleName cfg
38+ (modName', moduleDirPath) = case L.uncons (reverse $ T.splitOn "." modName) of
39+ Nothing -> error $ "writeStaticProject: invalid module name " <> T.unpack modName
40+ Just (name, parents) -> (name, target </> "src" </> T.unpack (T.intercalate "/" $ reverse parents))
41+ createDirectoryIfMissing True moduleDirPath
3442 modContents <- staticModuleFile modName paths
35− writeCabalProject target $ SimplePkg
36− { _simplePkg_name = _staticConfig_packageName cfg
37− , _simplePkg_moduleName = _staticConfig_moduleName cfg
38− , _simplePkg_moduleContents = modContents
39− , _simplePkg_dependencies = ["base", "ghc-prim", "text"]
40− }
43+ T.writeFile (moduleDirPath </> T.unpack modName' <.> "hs") modContents
44+
45+staticCabalFile :: StaticConfig -> Text
46+staticCabalFile cfg = T.unlines
47+ [ "name: " <> _staticConfig_packageName cfg
48+ , "version: 0"
49+ , "cabal-version: >= 1.2"
50+ , "build-type: Simple"
51+ , ""
52+ , "library"
53+ , " hs-source-dirs: src"
54+ , " build-depends:"
55+ , " base,"
56+ , " ghc-prim,"
57+ , " text"
58+ , " exposed-modules: " <> _staticConfig_moduleName cfg
59+ ]
4160
4261staticModuleFile :: Text -> Map FilePath FilePath -> IO Text
4362staticModuleFile moduleName paths = do
@@ -50,7 +69,7 @@ staticModuleFile moduleName paths = do
5069 , "{-# LANGUAGE OverloadedStrings #-}"
5170 , "{-# LANGUAGE ScopedTypeVariables #-}"
5271 , "{-# LANGUAGE TypeApplications #-}"
53− , "module " <> moduleName <> " {-# DEPRECATED \"Generate this module with the 'obelisk-asset-th-generate' executable instead.\" #-} where"
72+ , "module " <> moduleName <> " where"
5473 , ""
5574 , "import qualified GHC.Types"
5675 , "import Data.Text (Text)"
lib/asset/manifest/src/Obelisk/Asset/TH.hs+3−25
@@ -1,36 +1,14 @@
1−{-|
2−Description:
3− Template Haskell for generating asset paths.
4−-}
51module Obelisk.Asset.TH
62 ( assetPath
7− , staticAssetRaw
8− , staticAssetHashed
93 ) where
104
115import Obelisk.Asset.Gather
126
13−import Language.Haskell.TH
147import Language.Haskell.TH.Syntax
158import System.FilePath.Posix
169
17−-- | Produces the hashed path of a file
18−hashedAssetFilePath :: FilePath -> FilePath -> Q FilePath
19−hashedAssetFilePath root relativePath = do
20− qAddDependentFile $ root </> relativePath
21− runIO (toHashedPath root relativePath)
22−
2310-- | Produces a string literal with the hashed path of the file
2411assetPath :: FilePath -> FilePath -> Q Exp
25−assetPath root relativePath =
26− LitE . StringL <$> hashedAssetFilePath root relativePath
27−
28−staticPrefix :: FilePath
29−staticPrefix = "/static"
30−
31−staticAssetRaw :: FilePath -> Q Exp
32−staticAssetRaw fp = returnQ $ LitE $ StringL $ staticPrefix </> fp
33−
34−staticAssetHashed :: FilePath -> FilePath -> Q Exp
35−staticAssetHashed root fp = do
36− LitE . StringL . (staticPrefix </>) <$> hashedAssetFilePath root fp
12+assetPath root relativePath = do
13+ qAddDependentFile $ root </> relativePath
14+ LitE . StringL <$> runIO (toHashedPath root relativePath)
lib/cliapp/src/Obelisk/CliApp/Logging.hs+1−5
@@ -44,7 +44,7 @@ import Data.Text (Text)
4444import qualified Data.Text as T
4545import qualified Data.Text.IO as T
4646import GHC.IO.Encoding.Types
47−import System.Console.ANSI (Color (..), ColorIntensity (Vivid),
47+import System.Console.ANSI (Color (Red, Yellow), ColorIntensity (Vivid),
4848 ConsoleIntensity (FaintIntensity), ConsoleLayer (Foreground),
4949 SGR (SetColor, SetConsoleIntensity), clearLine)
5050import System.Exit (ExitCode (..))
@@ -184,8 +184,6 @@ writeLog withNewLine noColor (WithSeverity severity s) = if T.null s then pure (
184184 | noColor && severity <= Warning = liftIO $ putFn $ T.pack (show severity) <> ": " <> s
185185 | not noColor && severity <= Error = TS.putStrWithSGR errorColors h withNewLine s
186186 | not noColor && severity <= Warning = TS.putStrWithSGR warningColors h withNewLine s
187− | not noColor && severity == Notice = TS.putStrWithSGR noticeColors h withNewLine s
188− | not noColor && severity == Informational = TS.putStrWithSGR infoColors h withNewLine s
189187 | not noColor && severity >= Debug = TS.putStrWithSGR debugColors h withNewLine s
190188 | otherwise = liftIO $ putFn s
191189
@@ -193,8 +191,6 @@ writeLog withNewLine noColor (WithSeverity severity s) = if T.null s then pure (
193191 h = if severity <= Error then stderr else stdout
194192 errorColors = [SetColor Foreground Vivid Red]
195193 warningColors = [SetColor Foreground Vivid Yellow]
196− infoColors = [SetColor Foreground Vivid Green]
197− noticeColors = [SetColor Foreground Vivid Blue]
198194 debugColors = [SetConsoleIntensity FaintIntensity]
199195
200196-- | Allow the user to immediately switch to verbose logging upon pressing a particular key.
lib/command/obelisk-command.cabal−3
@@ -20,7 +20,6 @@ library
2020 , exceptions
2121 , extra
2222 , filepath
23− , fsnotify
2423 , git
2524 , github
2625 , here
@@ -40,8 +39,6 @@ library
4039 , optparse-applicative
4140 , placeholders
4241 , process
43− , reflex
44− , reflex-fsnotify
4542 , shell-escape
4643 , temporary
4744 , terminal-size
lib/command/src/Obelisk/Command/Project.hs+8−114
@@ -1,9 +1,6 @@
11{-# LANGUAGE FlexibleContexts #-}
22{-# LANGUAGE LambdaCase #-}
33{-# LANGUAGE OverloadedStrings #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5−{-# LANGUAGE TupleSections #-}
6−
74module Obelisk.Command.Project
85 ( InitSource (..)
96 , findProjectObeliskCommand
@@ -19,18 +16,12 @@ module Obelisk.Command.Project
1916 , toObeliskDir
2017 , withProjectRoot
2118 , bashEscape
22− , getHaskellManifestProjectPath
23− , AssetSource(..)
24− , describeImpureAssetSource
25− , watchStaticFilesDerivation
2619 ) where
2720
2821import Control.Concurrent.MVar (MVar, newMVar, withMVarMasked)
29−import Control.Lens ((.~), (?~), (<&>), (^.), _2, _3)
22+import Control.Lens ((.~), (?~), (<&>))
3023import Control.Monad
31−import Control.Monad.Except
3224import Control.Monad.IO.Class (liftIO)
33−import Control.Monad.Log
3425import Control.Monad.State
3526import qualified Data.Aeson as Json
3627import qualified Data.ByteString.UTF8 as BSU
@@ -43,26 +34,20 @@ import Data.Text (Text)
4334import qualified Data.Text as T
4435import Data.Text.Encoding (decodeUtf8, encodeUtf8)
4536import Data.Traversable (for)
46−import Reflex
47−import Reflex.FSNotify
48−import Reflex.Host.Headless
4937import System.Directory
5038import System.Environment (lookupEnv)
51−import System.Exit (ExitCode(..))
5239import System.FilePath
53−import System.FSNotify (defaultConfig, eventPath)
5440import System.IO.Temp
5541import System.IO.Unsafe (unsafePerformIO)
5642import System.PosixCompat.Files
5743import System.PosixCompat.Types
5844import System.PosixCompat.User
59−import qualified System.Process as Proc
6045import Text.ShellEscape (bash, bytes)
6146
6247import GitHub.Data.GitData (Branch)
6348import GitHub.Data.Name (Name)
6449
65−import Obelisk.App (MonadObelisk, runObelisk, getObelisk)
50+import Obelisk.App (MonadObelisk)
6651import Obelisk.CliApp
6752import Obelisk.Command.Nix
6853import Obelisk.Command.Thunk
@@ -349,21 +334,7 @@ nixShellWithHoogle root isPure shell' command = do
349334 \})).project.shells.${shell}"
350335 & nixShellConfig_common . nixCmdConfig_args .~ [ strArg "shell" shell' ]
351336
352−-- | Describes the provenance of static assets (i.e., are they the result of a derivation
353−-- that was built, or just a folder full of files.
354−data AssetSource = AssetSource_Derivation
355− | AssetSource_Files
356− deriving (Eq)
357−
358−-- | Some log messages to make it easier to tell where static files are coming from
359−describeImpureAssetSource :: AssetSource -> Text -> Text
360−describeImpureAssetSource src path = case src of
361− AssetSource_Files -> "Assets impurely loaded from: " <> path
362− AssetSource_Derivation -> "Assets derivation built and impurely loaded from: " <> path
363−
364−-- | Determine where the static files of a project are and whether they're plain files or a derivation.
365−-- If they are a derivation, that derivation will be built.
366−findProjectAssets :: MonadObelisk m => FilePath -> m (AssetSource, Text)
337+findProjectAssets :: MonadObelisk m => FilePath -> m Text
367338findProjectAssets root = do
368339 isDerivation <- readProcessAndLogStderr Debug $ setCwd (Just root) $
369340 proc nixExePath
@@ -375,87 +346,10 @@ findProjectAssets root = do
375346 ]
376347 -- Check whether the impure static files are a derivation (and so must be built)
377348 if isDerivation == "1"
378− then do
379− _ <- buildStaticFilesDerivationAndSymlink
380− (readProcessAndLogStderr Debug)
381− root
382− pure (AssetSource_Derivation, T.pack $ root </> "static.out")
383− else fmap (AssetSource_Files,) $ readProcessAndLogStderr Debug $ setCwd (Just root) $
384− proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
385−
386−-- | Get the nix store path to the generated static asset manifest module (e.g., "obelisk-generated-static")
387−getHaskellManifestProjectPath :: MonadObelisk m => FilePath -> m Text
388−getHaskellManifestProjectPath root = fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $
389− proc nixBuildExePath
390− [ "--no-out-link"
391− , "-E"
392− , "(let a = import ./. {}; in a.passthru.processedStatic.haskellManifest)"
393− ]
394−
395−-- | Watch the project directory for file changes and check whether those file changes
396−-- cause changes in the static files nix derivation. If so, rebuild it.
397−watchStaticFilesDerivation
398− :: (MonadIO m, MonadObelisk m)
399− => FilePath
400− -> m ()
401−watchStaticFilesDerivation root = do
402− ob <- getObelisk
403− drv0 <- showDerivation
404− liftIO $ runHeadlessApp $ do
405− pb <- getPostBuild
406− checkForChanges <- batchOccurrences 0.25 =<< watchDirectoryTree
407− defaultConfig
408− (root <$ pb)
409− ((/="static.out") . takeFileName . eventPath)
410− drv <- performEvent $ ffor checkForChanges $ \_ ->
411− liftIO $ runObelisk ob showDerivation
412− drvs <- foldDyn (\new (_, old, _) -> (old, new, old /= new)) (drv0, drv0, False) drv
413− out <- throttleBatchWithLag
414− (\e -> performEvent $ ffor e $ \_ ->
415− liftIO $ runObelisk ob $ buildStaticCatchErrors >> pure ())
416− (void $ fmapMaybe (^._2) $ ffilter (^._3) $ updated drvs)
417− performEvent_ $ ffor out $ \_ -> runObelisk ob $
418− putLog Notice "Static assets rebuilt and symlinked to static.out"
419− pure never
420− where
421− handleBuildFailure
422− :: MonadObelisk m
423− => (ExitCode, String, String)
424− -> m (Maybe Text)
425− handleBuildFailure (ex, out, err) = case ex of
426− ExitSuccess -> pure $ Just $ T.pack out
427− _ -> do
428− putLog Error $
429− "Static assets build failed: " <> T.pack err
430− pure Nothing
431− showDerivation :: MonadObelisk m => m (Maybe Text)
432− showDerivation =
433− handleBuildFailure <=< readCreateProcessWithExitCode $
434− setCwd (Just root) $ ProcessSpec
435− { _processSpec_createProcess = Proc.proc nixExePath
436− [ "show-derivation"
437− , "-f", "."
438− , "passthru.staticFilesImpure"
439− ]
440− , _processSpec_overrideEnv = Nothing
441− }
442− buildStaticCatchErrors :: MonadObelisk m => m (Maybe Text)
443− buildStaticCatchErrors = handleBuildFailure =<<
444− buildStaticFilesDerivationAndSymlink
445− readCreateProcessWithExitCode
446− root
447−
448−buildStaticFilesDerivationAndSymlink
449− :: MonadObelisk m
450− => (ProcessSpec -> m a)
451− -> FilePath
452− -> m a
453−buildStaticFilesDerivationAndSymlink f root = f $
454− setCwd (Just root) $ ProcessSpec
455− { _processSpec_createProcess = Proc.proc
456− nixBuildExePath
457− [ "-o", "static.out"
349+ then fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $ -- Strip whitespace here because nix-build has no --raw option
350+ proc nixBuildExePath
351+ [ "--no-out-link"
458352 , "-E", "(import ./. {}).passthru.staticFilesImpure"
459353 ]
460− , _processSpec_overrideEnv = Nothing
461− }
354+ else readProcessAndLogStderr Debug $ setCwd (Just root) $
355+ proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
lib/command/src/Obelisk/Command/Run.hs+10−25
@@ -14,8 +14,7 @@ module Obelisk.Command.Run where
1414import Control.Arrow ((&&&))
1515import Control.Exception (Exception, bracket)
1616import Control.Lens (ifor, (.~), (&))
17−import Control.Concurrent (forkIO)
18−import Control.Monad (filterM, void)
17+import Control.Monad (filterM)
1918import Control.Monad.Except (runExceptT, throwError)
2019import Control.Monad.IO.Class (liftIO)
2120import Control.Monad.Reader (MonadIO)
@@ -64,7 +63,7 @@ import System.FilePath
6463import qualified System.Info
6564import System.IO.Temp (withSystemTempDirectory)
6665
67−import Obelisk.App (MonadObelisk, getObelisk, runObelisk)
66+import Obelisk.App (MonadObelisk)
6867import Obelisk.CliApp (
6968 Severity (..),
7069 failWith,
@@ -78,7 +77,7 @@ import Obelisk.CliApp (
7877 withSpinner,
7978 )
8079import Obelisk.Command.Nix
81−import Obelisk.Command.Project
80+import Obelisk.Command.Project (nixShellWithoutPkgs, withProjectRoot, findProjectAssets, bashEscape)
8281import Obelisk.Command.Thunk (attrCacheFileName)
8382import Obelisk.Command.Utils (findExePath, ghcidExePath)
8483
@@ -145,8 +144,8 @@ profile profileBasePattern rtsFlags = withProjectRoot "." $ \root -> do
145144 , _target_attr = Just "__unstable__.profiledObRun"
146145 , _target_expr = Nothing
147146 }
148− (assetType, assets) <- findProjectAssets root
149− putLog Debug $ describeImpureAssetSource assetType assets
147+ assets <- findProjectAssets root
148+ putLog Debug $ "Assets impurely loaded from: " <> assets
150149 time <- liftIO getCurrentTime
151150 let profileBaseName = formatTime defaultTimeLocale profileBasePattern time
152151 liftIO $ createDirectoryIfMissing True $ takeDirectory $ root </> profileBaseName
@@ -164,16 +163,9 @@ profile profileBasePattern rtsFlags = withProjectRoot "." $ \root -> do
164163run :: MonadObelisk m => FilePath -> PathTree Interpret -> m ()
165164run root interpretPaths = do
166165 pkgs <- getParsedLocalPkgs root interpretPaths
167− (assetType, assets) <- findProjectAssets root
168− manifestPkg <- parsePackagesOrFail . (:[]) . T.unpack =<< getHaskellManifestProjectPath root
169− putLog Debug $ describeImpureAssetSource assetType assets
170− case assetType of
171− AssetSource_Derivation -> do
172− ob <- getObelisk
173− putLog Debug "Starting static file derivation watcher..."
174− void $ liftIO $ forkIO $ runObelisk ob $ watchStaticFilesDerivation root
175− _ -> pure ()
176− ghciArgs <- getGhciSessionSettings (pkgs <> manifestPkg) root True
166+ assets <- findProjectAssets root
167+ putLog Debug $ "Assets impurely loaded from: " <> assets
168+ ghciArgs <- getGhciSessionSettings pkgs root True
177169 freePort <- getFreePort
178170 withGhciScriptArgs pkgs $ \dotGhciArgs -> do
179171 runGhcid root True (ghciArgs <> dotGhciArgs) pkgs $ Just $ unwords
@@ -230,12 +222,7 @@ getLocalPkgs root interpretPaths = do
230222 let rootsAndExclusions = calcIntepretFinds "" interpretPaths
231223
232224 fmap fold $ for (MMap.toAscList rootsAndExclusions) $ \(interpretPathRoot, exclusions) ->
233− let allExclusions = obeliskPackageExclusions
234− <> exclusions
235− <> Set.singleton ("*" </> attrCacheFileName)
236− <> Set.singleton ("*" </> "lib/asset/manifest") -- NB: obelisk-asset-manifest is excluded because it generates
237− -- a module that in turn imports it. This will cause ob run to
238− -- fail in its current implementation.
225+ let allExclusions = obeliskPackageExclusions <> exclusions <> Set.singleton ("*" </> attrCacheFileName)
239226 in fmap (Set.fromList . map normalise) $ runFind $
240227 ["-L", interpretPathRoot, "(", "-name", "*.cabal", "-o", "-name", Hpack.packageConfig, ")", "-a", "-type", "f"]
241228 <> concat [["-not", "-path", p </> "*"] | p <- toList allExclusions]
@@ -413,8 +400,7 @@ withGhciScriptArgs
413400 => f CabalPackageInfo -- ^ List of packages to load into ghci
414401 -> ([String] -> m ()) -- ^ Action to run with the extra ghci arguments
415402 -> m ()
416−withGhciScriptArgs packageInfos f = withGhciScript loadPreludeManually packageInfos $ \fp ->
417− f ["-XNoImplicitPrelude", "-ghci-script", fp]
403+withGhciScriptArgs packageInfos f = withGhciScript loadPreludeManually packageInfos $ \fp -> f ["-XNoImplicitPrelude", "-ghci-script", fp]
418404 where
419405 -- These lines must be first and allow the session to support a custom Prelude when @-XNoImplicitPrelude@
420406 -- is passed to the ghci session.
@@ -472,7 +458,6 @@ getGhciSessionSettings (toList -> packageInfos) pathBase useRelativePaths = do
472458
473459 pure
474460 $ baseGhciOptions
475− <> ["-DOBELISK_ASSET_PASSTHRU"] -- For passthrough static assets
476461 <> ["-F", "-pgmF", selfExe, "-optF", preprocessorIdentifier]
477462 <> concatMap (\p -> ["-optF", p]) pkgFiles
478463 <> [ "-i" <> intercalate ":" (concatMap toList pkgSrcPaths) ]
skeleton/frontend/src/Frontend.hs+4−3
@@ -1,5 +1,6 @@
1+{-# LANGUAGE DataKinds #-}
12{-# LANGUAGE OverloadedStrings #-}
2−{-# LANGUAGE TemplateHaskell #-}
3+{-# LANGUAGE TypeApplications #-}
34
45module Frontend where
56
@@ -26,7 +27,7 @@ frontend :: Frontend (R FrontendRoute)
2627frontend = Frontend
2728 { _frontend_head = do
2829 el "title" $ text "Obelisk Minimal Example"
29− elAttr "link" ("href" =: $(static "main.css") <> "type" =: "text/css" <> "rel" =: "stylesheet") blank
30+ elAttr "link" ("href" =: static @"main.css" <> "type" =: "text/css" <> "rel" =: "stylesheet") blank
3031 , _frontend_body = do
3132 el "h1" $ text "Welcome to Obelisk!"
3233 el "p" $ text $ T.pack commonStuff
@@ -37,7 +38,7 @@ frontend = Frontend
3738 -- print "Hello, World!" on the client.
3839 prerender_ blank $ liftJSM $ void $ eval ("console.log('Hello, World!')" :: T.Text)
3940
40− elAttr "img" ("src" =: $(static "obelisk.jpg")) blank
41+ elAttr "img" ("src" =: static @"obelisk.jpg") blank
4142 el "div" $ do
4243 exampleConfig <- getConfig "common/example"
4344 case exampleConfig of