on 6573d48 help sign in
ChangeLog.md+7
since splitsnapshots
unifiedsplit
@@ -2,6 +2,13 @@
2
2
3This 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.
3This 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.
4
4
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+
5## v0.9.1.0
12## v0.9.1.0
6
13
7* [#801](https://github.com/obsidiansystems/obelisk/pull/801): Remove errors and warning for local packages without a library component
14* [#801](https://github.com/obsidiansystems/obelisk/pull/801): Remove errors and warning for local packages without a library component
default.nix+10−4
@@ -65,7 +65,7 @@ in rec {
65 '';
65 '';
66 nullIfAbsent = p: if lib.pathExists p then p else null;
66 nullIfAbsent = p: if lib.pathExists p then p else null;
67 #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.
67 #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" }: pkgs.runCommand "asset-manifest" {
68+ processAssets = { src, packageName ? "obelisk-generated-static", moduleName ? "Obelisk.Generated.Static", exe ? "obelisk-asset-th-generate" }: pkgs.runCommand "asset-manifest" {
69 inherit src;
69 inherit src;
70 outputs = [ "out" "haskellManifest" "symlinked" ];
70 outputs = [ "out" "haskellManifest" "symlinked" ];
71 nativeBuildInputs = [ ghcObelisk.obelisk-asset-manifest ];
71 nativeBuildInputs = [ ghcObelisk.obelisk-asset-manifest ];
@@ -73,7 +73,7 @@ in rec {
73 set -euo pipefail
73 set -euo pipefail
74 touch "$out"
74 touch "$out"
75 mkdir -p "$symlinked"
75 mkdir -p "$symlinked"
76− obelisk-asset-manifest-generate "$src" "$haskellManifest" ${packageName} ${moduleName} "$symlinked"
76+ ${exe} "$src" "$haskellManifest" ${packageName} ${moduleName} "$symlinked"
77 '';
77 '';
78
78
79 compressedJs = frontend: optimizationLevel: pkgs.runCommand "compressedJs" {} ''
79 compressedJs = frontend: optimizationLevel: pkgs.runCommand "compressedJs" {} ''
@@ -237,13 +237,14 @@ in rec {
237 , withHoogle ? false # Setting this to `true` makes shell reloading far slower
237 , withHoogle ? false # Setting this to `true` makes shell reloading far slower
238 , __closureCompilerOptimizationLevel ? "ADVANCED" # Set this to `null` to skip the closure-compiler step
238 , __closureCompilerOptimizationLevel ? "ADVANCED" # Set this to `null` to skip the closure-compiler step
239 , __withGhcide ? false
239 , __withGhcide ? false
240+ , __deprecated ? {}
240 }:
241 }:
241 let
242 let
242 allConfig = nixpkgs.lib.makeExtensible (self: {
243 allConfig = nixpkgs.lib.makeExtensible (self: {
243 base = base';
244 base = base';
244 inherit args;
245 inherit args;
245 userSettings = {
246 userSettings = {
246− inherit android ios packages overrides tools shellToolOverrides withHoogle __closureCompilerOptimizationLevel __withGhcide;
247+ inherit android ios packages overrides tools shellToolOverrides withHoogle __closureCompilerOptimizationLevel __withGhcide __deprecated;
247 staticFiles = if staticFiles == null then self.base + /static else staticFiles;
248 staticFiles = if staticFiles == null then self.base + /static else staticFiles;
248 };
249 };
249 frontendName = "frontend";
250 frontendName = "frontend";
@@ -251,7 +252,12 @@ in rec {
251 commonName = "common";
252 commonName = "common";
252 staticName = "obelisk-generated-static";
253 staticName = "obelisk-generated-static";
253 staticFilesImpure = let fs = self.userSettings.staticFiles; in if lib.isDerivation fs then fs else toString fs;
254 staticFilesImpure = let fs = self.userSettings.staticFiles; in if lib.isDerivation fs then fs else toString fs;
255+ processedStatic = processAssets {
254− processedStatic = processAssets { src = self.userSettings.staticFiles; };
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+ };
255 # The packages whose names and roles are defined by this package
261 # The packages whose names and roles are defined by this package
256 predefinedPackages = lib.filterAttrs (_: x: x != null) {
262 predefinedPackages = lib.filterAttrs (_: x: x != null) {
257 ${self.frontendName} = nullIfAbsent (self.base + "/frontend");
263 ${self.frontendName} = nullIfAbsent (self.base + "/frontend");
lib/asset/README.md+2−2
@@ -5,7 +5,7 @@
5
5
6**Efficiently served, aggressively cached static web resources.**
6**Efficiently served, aggressively cached static web resources.**
7
7
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)
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.
9
9
10```nix
10```nix
11{ assets ? import ./../assets.nix { inherit nixpkgs; } }:
11{ 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
19myAssets = assets.mkAssets ./static;
19myAssets = assets.mkAssets ./static;
20```
20```
21
21
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.
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.
23
23
24```bash
24```bash
25nix-build -o static.assets -A myAssets
25nix-build -o static.assets -A myAssets
lib/asset/manifest/obelisk-asset-manifest.cabal+11−1
@@ -27,6 +27,7 @@ library
27 , unix-compat
27 , unix-compat
28 , vector
28 , vector
29 exposed-modules:
29 exposed-modules:
30+ Obelisk.Asset.Cabal
30 Obelisk.Asset.Gather
31 Obelisk.Asset.Gather
31 Obelisk.Asset.Promoted
32 Obelisk.Asset.Promoted
32 Obelisk.Asset.Symlink
33 Obelisk.Asset.Symlink
@@ -35,7 +36,7 @@ library
35 other-extensions: TemplateHaskell
36 other-extensions: TemplateHaskell
36 ghc-options:
37 ghc-options:
37 -Wall -Werror -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O2
38 -Wall -Werror -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O2
38− -fno-warn-unused-do-bind -funbox-strict-fields -fprof-auto-calls
39+ -fno-warn-unused-do-bind -funbox-strict-fields -fprof-auto-calls
39
40
40executable obelisk-asset-manifest-generate
41executable obelisk-asset-manifest-generate
41 hs-source-dirs: src-bin
42 hs-source-dirs: src-bin
@@ -44,3 +45,12 @@ executable obelisk-asset-manifest-generate
44 base
45 base
45 , obelisk-asset-manifest
46 , obelisk-asset-manifest
46 , text
47 , 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.hsnew file+41
@@ -0,0 +1,41 @@
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.hsnew file+42
@@ -0,0 +1,42 @@
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
6 , toHashedPath
6 , toHashedPath
7 ) where
7 ) where
8
8
9−import Control.DeepSeq
9+import Control.DeepSeq (force)
10import Control.Monad (forM)
10import Control.Monad (forM)
11−import Data.Bits
11+import Data.Bits (shift, (.|.), (.&.))
12−import qualified Data.ByteString.Lazy as LBS
12+import qualified Data.ByteString.Lazy as LBS (readFile, toStrict, ByteString, length, index)
13−import qualified Data.ByteString.Lazy.Builder as LBS
13+import qualified Data.ByteString.Lazy.Builder as LBS (toLazyByteString, word8)
14−import Data.Char
14+import Data.Char (ord)
15−import Data.Digest.Pure.SHA
15+import Data.Digest.Pure.SHA (bytestringDigest, sha256)
16import Data.Map (Map)
16import Data.Map (Map)
17−import qualified Data.Map as Map
17+import qualified Data.Map as Map (singleton)
18−import qualified Data.Text as T
18+import qualified Data.Text as T (unpack)
19−import Data.Text.Encoding
19+import Data.Text.Encoding (decodeUtf8)
20−import qualified Data.Vector.Unboxed as UV
20+import qualified Data.Vector.Unboxed as UV ((!), fromList)
21−import Data.Word
21+import Data.Word (Word8)
22−import System.FilePath.Posix
22+import System.FilePath.Posix ((</>), splitFileName, normalise)
23−import System.Directory
23+import System.Directory (listDirectory, doesFileExist)
24
24
25-- | Given a path, recursively explore it, creating hashed paths for all files found
25-- | Given a path, recursively explore it, creating hashed paths for all files found
26gatherHashedPaths
26gatherHashedPaths
lib/asset/manifest/src/Obelisk/Asset/Promoted.hs+8−27
@@ -10,7 +10,6 @@ module Obelisk.Asset.Promoted
10import Obelisk.Asset.Gather
10import Obelisk.Asset.Gather
11
11
12import Data.Foldable
12import Data.Foldable
13−import qualified Data.List as L
14import Language.Haskell.TH (runQ, pprint)
13import Language.Haskell.TH (runQ, pprint)
15import Language.Haskell.TH.Syntax hiding (lift)
14import Language.Haskell.TH.Syntax hiding (lift)
16import GHC.TypeLits
15import GHC.TypeLits
@@ -18,12 +17,11 @@ import Data.Sequence (Seq)
18import qualified Data.Sequence as Seq
17import qualified Data.Sequence as Seq
19import Control.Monad.Trans.Writer
18import Control.Monad.Trans.Writer
20import System.FilePath
19import System.FilePath
21−import System.Directory
22import Data.Text (Text)
20import Data.Text (Text)
23import qualified Data.Text as T
21import qualified Data.Text as T
24−import qualified Data.Text.IO as T
25import Data.Map (Map)
22import Data.Map (Map)
26import qualified Data.Map as Map
23import qualified Data.Map as Map
24+import Obelisk.Asset.Cabal (writeCabalProject, SimplePkg(..))
27
25
28data StaticConfig = StaticConfig
26data StaticConfig = StaticConfig
29 { _staticConfig_packageName :: Text --TODO: Better type
27 { _staticConfig_packageName :: Text --TODO: Better type
@@ -32,31 +30,14 @@ data StaticConfig = StaticConfig
32
30
33writeStaticProject :: Map FilePath FilePath -> FilePath -> StaticConfig -> IO ()
31writeStaticProject :: Map FilePath FilePath -> FilePath -> StaticConfig -> IO ()
34writeStaticProject paths target cfg = do
32writeStaticProject paths target cfg = do
35− createDirectoryIfMissing True target
36− T.writeFile (target </> T.unpack (_staticConfig_packageName cfg) <.> "cabal") $ staticCabalFile cfg
37 let modName = _staticConfig_moduleName cfg
33 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
42 modContents <- staticModuleFile modName paths
34 modContents <- staticModuleFile modName paths
43− T.writeFile (moduleDirPath </> T.unpack modName' <.> "hs") modContents
35+ writeCabalProject target $ SimplePkg
44−
36+ { _simplePkg_name = _staticConfig_packageName cfg
45−staticCabalFile :: StaticConfig -> Text
37+ , _simplePkg_moduleName = _staticConfig_moduleName cfg
46−staticCabalFile cfg = T.unlines
38+ , _simplePkg_moduleContents = modContents
47− [ "name: " <> _staticConfig_packageName cfg
39+ , _simplePkg_dependencies = ["base", "ghc-prim", "text"]
48− , "version: 0"
40+ }
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− ]
60
41
61staticModuleFile :: Text -> Map FilePath FilePath -> IO Text
42staticModuleFile :: Text -> Map FilePath FilePath -> IO Text
62staticModuleFile moduleName paths = do
43staticModuleFile moduleName paths = do
@@ -69,7 +50,7 @@ staticModuleFile moduleName paths = do
69 , "{-# LANGUAGE OverloadedStrings #-}"
50 , "{-# LANGUAGE OverloadedStrings #-}"
70 , "{-# LANGUAGE ScopedTypeVariables #-}"
51 , "{-# LANGUAGE ScopedTypeVariables #-}"
71 , "{-# LANGUAGE TypeApplications #-}"
52 , "{-# LANGUAGE TypeApplications #-}"
72− , "module " <> moduleName <> " where"
53+ , "module " <> moduleName <> " {-# DEPRECATED \"Generate this module with the 'obelisk-asset-th-generate' executable instead.\" #-} where"
73 , ""
54 , ""
74 , "import qualified GHC.Types"
55 , "import qualified GHC.Types"
75 , "import Data.Text (Text)"
56 , "import Data.Text (Text)"
lib/asset/manifest/src/Obelisk/Asset/TH.hs+25−3
@@ -1,14 +1,36 @@
1+{-|
2+Description:
3+ Template Haskell for generating asset paths.
4+-}
1module Obelisk.Asset.TH
5module Obelisk.Asset.TH
2 ( assetPath
6 ( assetPath
7+ , staticAssetRaw
8+ , staticAssetHashed
3 ) where
9 ) where
4
10
5import Obelisk.Asset.Gather
11import Obelisk.Asset.Gather
6
12
13+import Language.Haskell.TH
7import Language.Haskell.TH.Syntax
14import Language.Haskell.TH.Syntax
8import System.FilePath.Posix
15import System.FilePath.Posix
9
16
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+
10-- | Produces a string literal with the hashed path of the file
23-- | Produces a string literal with the hashed path of the file
11assetPath :: FilePath -> FilePath -> Q Exp
24assetPath :: FilePath -> FilePath -> Q Exp
12−assetPath root relativePath = do
25+assetPath root relativePath =
13− qAddDependentFile $ root </> relativePath
26+ LitE . StringL <$> hashedAssetFilePath root relativePath
14− LitE . StringL <$> runIO (toHashedPath 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
lib/command/src/Obelisk/Command/Project.hs+9
@@ -16,6 +16,7 @@ module Obelisk.Command.Project
16 , toObeliskDir
16 , toObeliskDir
17 , withProjectRoot
17 , withProjectRoot
18 , bashEscape
18 , bashEscape
19+ , getHaskellManifestProjectPath
19 ) where
20 ) where
20
21
21import Control.Concurrent.MVar (MVar, newMVar, withMVarMasked)
22import Control.Concurrent.MVar (MVar, newMVar, withMVarMasked)
@@ -353,3 +354,11 @@ findProjectAssets root = do
353 ]
354 ]
354 else readProcessAndLogStderr Debug $ setCwd (Just root) $
355 else readProcessAndLogStderr Debug $ setCwd (Just root) $
355 proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
356 proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
357+
358+getHaskellManifestProjectPath :: MonadObelisk m => FilePath -> m Text
359+getHaskellManifestProjectPath root = fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $
360+ proc nixBuildExePath
361+ [ "--no-out-link"
362+ , "-E"
363+ , "(let a = import ./. {}; in a.passthru.processedStatic.haskellManifest)"
364+ ]
lib/command/src/Obelisk/Command/Run.hs+12−4
@@ -77,7 +77,7 @@ import Obelisk.CliApp (
77 withSpinner,
77 withSpinner,
78 )
78 )
79import Obelisk.Command.Nix
79import Obelisk.Command.Nix
80−import Obelisk.Command.Project (nixShellWithoutPkgs, withProjectRoot, findProjectAssets, bashEscape)
80+import Obelisk.Command.Project (nixShellWithoutPkgs, withProjectRoot, findProjectAssets, bashEscape, getHaskellManifestProjectPath)
81import Obelisk.Command.Thunk (attrCacheFileName)
81import Obelisk.Command.Thunk (attrCacheFileName)
82import Obelisk.Command.Utils (findExePath, ghcidExePath)
82import Obelisk.Command.Utils (findExePath, ghcidExePath)
83
83
@@ -164,8 +164,9 @@ run :: MonadObelisk m => FilePath -> PathTree Interpret -> m ()
164run root interpretPaths = do
164run root interpretPaths = do
165 pkgs <- getParsedLocalPkgs root interpretPaths
165 pkgs <- getParsedLocalPkgs root interpretPaths
166 assets <- findProjectAssets root
166 assets <- findProjectAssets root
167+ manifestPkg <- parsePackagesOrFail . (:[]) . T.unpack =<< getHaskellManifestProjectPath root
167 putLog Debug $ "Assets impurely loaded from: " <> assets
168 putLog Debug $ "Assets impurely loaded from: " <> assets
168− ghciArgs <- getGhciSessionSettings pkgs root True
169+ ghciArgs <- getGhciSessionSettings (pkgs <> manifestPkg) root True
169 freePort <- getFreePort
170 freePort <- getFreePort
170 withGhciScriptArgs pkgs $ \dotGhciArgs -> do
171 withGhciScriptArgs pkgs $ \dotGhciArgs -> do
171 runGhcid root True (ghciArgs <> dotGhciArgs) pkgs $ Just $ unwords
172 runGhcid root True (ghciArgs <> dotGhciArgs) pkgs $ Just $ unwords
@@ -222,7 +223,12 @@ getLocalPkgs root interpretPaths = do
222 let rootsAndExclusions = calcIntepretFinds "" interpretPaths
223 let rootsAndExclusions = calcIntepretFinds "" interpretPaths
223
224
224 fmap fold $ for (MMap.toAscList rootsAndExclusions) $ \(interpretPathRoot, exclusions) ->
225 fmap fold $ for (MMap.toAscList rootsAndExclusions) $ \(interpretPathRoot, exclusions) ->
225− let allExclusions = obeliskPackageExclusions <> exclusions <> Set.singleton ("*" </> attrCacheFileName)
226+ let allExclusions = obeliskPackageExclusions
227+ <> exclusions
228+ <> Set.singleton ("*" </> attrCacheFileName)
229+ <> Set.singleton ("*" </> "lib/asset/manifest") -- NB: obelisk-asset-manifest is excluded because it generates
230+ -- a module that in turn imports it. This will cause ob run to
231+ -- fail in its current implementation.
226 in fmap (Set.fromList . map normalise) $ runFind $
232 in fmap (Set.fromList . map normalise) $ runFind $
227 ["-L", interpretPathRoot, "(", "-name", "*.cabal", "-o", "-name", Hpack.packageConfig, ")", "-a", "-type", "f"]
233 ["-L", interpretPathRoot, "(", "-name", "*.cabal", "-o", "-name", Hpack.packageConfig, ")", "-a", "-type", "f"]
228 <> concat [["-not", "-path", p </> "*"] | p <- toList allExclusions]
234 <> concat [["-not", "-path", p </> "*"] | p <- toList allExclusions]
@@ -400,7 +406,8 @@ withGhciScriptArgs
400 => f CabalPackageInfo -- ^ List of packages to load into ghci
406 => f CabalPackageInfo -- ^ List of packages to load into ghci
401 -> ([String] -> m ()) -- ^ Action to run with the extra ghci arguments
407 -> ([String] -> m ()) -- ^ Action to run with the extra ghci arguments
402 -> m ()
408 -> m ()
403−withGhciScriptArgs packageInfos f = withGhciScript loadPreludeManually packageInfos $ \fp -> f ["-XNoImplicitPrelude", "-ghci-script", fp]
409+withGhciScriptArgs packageInfos f = withGhciScript loadPreludeManually packageInfos $ \fp ->
410+ f ["-XNoImplicitPrelude", "-ghci-script", fp]
404 where
411 where
405 -- These lines must be first and allow the session to support a custom Prelude when @-XNoImplicitPrelude@
412 -- These lines must be first and allow the session to support a custom Prelude when @-XNoImplicitPrelude@
406 -- is passed to the ghci session.
413 -- is passed to the ghci session.
@@ -458,6 +465,7 @@ getGhciSessionSettings (toList -> packageInfos) pathBase useRelativePaths = do
458
465
459 pure
466 pure
460 $ baseGhciOptions
467 $ baseGhciOptions
468+ <> ["-DOBELISK_ASSET_PASSTHRU"] -- For passthrough static assets
461 <> ["-F", "-pgmF", selfExe, "-optF", preprocessorIdentifier]
469 <> ["-F", "-pgmF", selfExe, "-optF", preprocessorIdentifier]
462 <> concatMap (\p -> ["-optF", p]) pkgFiles
470 <> concatMap (\p -> ["-optF", p]) pkgFiles
463 <> [ "-i" <> intercalate ":" (concatMap toList pkgSrcPaths) ]
471 <> [ "-i" <> intercalate ":" (concatMap toList pkgSrcPaths) ]
skeleton/frontend/src/Frontend.hs+3−4
@@ -1,6 +1,5 @@
1−{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE TypeApplications #-}
2+{-# LANGUAGE TemplateHaskell #-}
4
3
5module Frontend where
4module Frontend where
6
5
@@ -27,7 +26,7 @@ frontend :: Frontend (R FrontendRoute)
27frontend = Frontend
26frontend = Frontend
28 { _frontend_head = do
27 { _frontend_head = do
29 el "title" $ text "Obelisk Minimal Example"
28 el "title" $ text "Obelisk Minimal Example"
30− elAttr "link" ("href" =: static @"main.css" <> "type" =: "text/css" <> "rel" =: "stylesheet") blank
29+ elAttr "link" ("href" =: $(static "main.css") <> "type" =: "text/css" <> "rel" =: "stylesheet") blank
31 , _frontend_body = do
30 , _frontend_body = do
32 el "h1" $ text "Welcome to Obelisk!"
31 el "h1" $ text "Welcome to Obelisk!"
33 el "p" $ text $ T.pack commonStuff
32 el "p" $ text $ T.pack commonStuff
@@ -38,7 +37,7 @@ frontend = Frontend
38 -- print "Hello, World!" on the client.
37 -- print "Hello, World!" on the client.
39 prerender_ blank $ liftJSM $ void $ eval ("console.log('Hello, World!')" :: T.Text)
38 prerender_ blank $ liftJSM $ void $ eval ("console.log('Hello, World!')" :: T.Text)
40
39
41− elAttr "img" ("src" =: static @"obelisk.jpg") blank
40+ elAttr "img" ("src" =: $(static "obelisk.jpg")) blank
42 el "div" $ do
41 el "div" $ do
43 exampleConfig <- getConfig "common/example"
42 exampleConfig <- getConfig "common/example"
44 case exampleConfig of
43 case exampleConfig of